<?php

namespace App\Services\Moderation;

use App\Enums\ModerationActionType;
use App\Enums\ModerationContentType;
use App\Enums\ModerationStatus;
use App\Models\Artwork;
use App\Models\ArtworkComment;
use App\Models\ContentModerationFinding;
use App\Models\User;
use Illuminate\Support\Facades\DB;

class ContentModerationReviewService
{
    public function __construct(
        private readonly ContentModerationActionLogService $actionLogs,
        private readonly DomainReputationService $domains,
        private readonly ModerationFeedbackService $feedback,
    ) {
    }

    public function markSafe(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): void
    {
        $this->updateFinding($finding, ModerationStatus::ReviewedSafe, $reviewer, $notes, ModerationActionType::MarkSafe);
        $this->feedback->record($finding->fresh(), 'marked_safe', $reviewer, $notes);
    }

    public function confirmSpam(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): void
    {
        $this->updateFinding($finding, ModerationStatus::ConfirmedSpam, $reviewer, $notes, ModerationActionType::ConfirmSpam);
        $this->domains->trackDomains((array) $finding->matched_domains_json, true, true);
        $this->feedback->record($finding->fresh(), 'confirmed_spam', $reviewer, $notes);
    }

    public function ignore(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): void
    {
        $this->updateFinding($finding, ModerationStatus::Ignored, $reviewer, $notes, ModerationActionType::Ignore);
    }

    public function resolve(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): void
    {
        $this->updateFinding($finding, ModerationStatus::Resolved, $reviewer, $notes, ModerationActionType::Resolve);
        $this->feedback->record($finding->fresh(), 'resolved', $reviewer, $notes);
    }

    public function markFalsePositive(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): void
    {
        DB::transaction(function () use ($finding, $reviewer, $notes): void {
            $oldVisibility = null;
            $newVisibility = null;

            if (in_array($finding->content_type, [ModerationContentType::ArtworkComment, ModerationContentType::ArtworkDescription, ModerationContentType::ArtworkTitle], true)) {
                [$action, $oldVisibility, $newVisibility] = match ($finding->content_type) {
                    ModerationContentType::ArtworkComment => $this->restoreComment($finding),
                    default => $this->restoreArtwork($finding),
                };

                $actionType = $action;
            } else {
                $actionType = ModerationActionType::MarkFalsePositive;
            }

            $oldStatus = $finding->status->value;
            $finding->forceFill([
                'status' => ModerationStatus::ReviewedSafe,
                'reviewed_by' => $reviewer->id,
                'reviewed_at' => now(),
                'resolved_by' => $reviewer->id,
                'resolved_at' => now(),
                'restored_by' => $oldVisibility !== null ? $reviewer->id : $finding->restored_by,
                'restored_at' => $oldVisibility !== null ? now() : $finding->restored_at,
                'is_auto_hidden' => false,
                'is_false_positive' => true,
                'false_positive_count' => ((int) $finding->false_positive_count) + 1,
                'action_taken' => ModerationActionType::MarkFalsePositive->value,
                'admin_notes' => $this->normalizeNotes($notes, $finding->admin_notes),
            ])->save();

            $this->actionLogs->log(
                $finding,
                $finding->content_type->value,
                $finding->content_id,
                ModerationActionType::MarkFalsePositive->value,
                $reviewer,
                $oldStatus,
                ModerationStatus::ReviewedSafe->value,
                $oldVisibility,
                $newVisibility,
                $notes,
                ['restored_action' => $actionType->value],
            );

            $this->feedback->record($finding->fresh(), 'false_positive', $reviewer, $notes, ['restored' => $oldVisibility !== null]);
        });
    }

    public function hideContent(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): ModerationActionType
    {
        return DB::transaction(function () use ($finding, $reviewer, $notes): ModerationActionType {
            [$action, $oldVisibility, $newVisibility] = match ($finding->content_type) {
                ModerationContentType::ArtworkComment => $this->hideComment($finding, false),
                ModerationContentType::ArtworkDescription, ModerationContentType::ArtworkTitle => $this->hideArtwork($finding, false),
            };

            $this->updateFinding($finding, ModerationStatus::ConfirmedSpam, $reviewer, $notes, $action, $oldVisibility, $newVisibility);
            $this->domains->trackDomains((array) $finding->matched_domains_json, true, true);

            return $action;
        });
    }

    public function autoHideContent(ContentModerationFinding $finding, ?string $notes = null): ModerationActionType
    {
        return DB::transaction(function () use ($finding, $notes): ModerationActionType {
            [$action, $oldVisibility, $newVisibility] = match ($finding->content_type) {
                ModerationContentType::ArtworkComment => $this->hideComment($finding, true),
                ModerationContentType::ArtworkDescription, ModerationContentType::ArtworkTitle => $this->hideArtwork($finding, true),
            };

            $finding->forceFill([
                'is_auto_hidden' => true,
                'auto_action_taken' => $action->value,
                'auto_hidden_at' => \now(),
                'action_taken' => $action->value,
                'admin_notes' => $this->normalizeNotes($notes, $finding->admin_notes),
            ])->save();

            $this->actionLogs->log(
                $finding,
                $finding->content_type->value,
                $finding->content_id,
                $action->value,
                null,
                $finding->status->value,
                $finding->status->value,
                $oldVisibility,
                $newVisibility,
                $notes,
                ['automated' => true],
            );

            $this->domains->trackDomains((array) $finding->matched_domains_json, true, false);

            return $action;
        });
    }

    public function restoreContent(ContentModerationFinding $finding, User $reviewer, ?string $notes = null): ModerationActionType
    {
        return DB::transaction(function () use ($finding, $reviewer, $notes): ModerationActionType {
            [$action, $oldVisibility, $newVisibility] = match ($finding->content_type) {
                ModerationContentType::ArtworkComment => $this->restoreComment($finding),
                ModerationContentType::ArtworkDescription, ModerationContentType::ArtworkTitle => $this->restoreArtwork($finding),
            };

            $oldStatus = $finding->status->value;

            $finding->forceFill([
                'status' => ModerationStatus::ReviewedSafe,
                'reviewed_by' => $reviewer->id,
                'reviewed_at' => \now(),
                'resolved_by' => $reviewer->id,
                'resolved_at' => \now(),
                'restored_by' => $reviewer->id,
                'restored_at' => \now(),
                'is_auto_hidden' => false,
                'action_taken' => $action->value,
                'admin_notes' => $this->normalizeNotes($notes, $finding->admin_notes),
            ])->save();

            $this->actionLogs->log(
                $finding,
                $finding->content_type->value,
                $finding->content_id,
                $action->value,
                $reviewer,
                $oldStatus,
                ModerationStatus::ReviewedSafe->value,
                $oldVisibility,
                $newVisibility,
                $notes,
                ['restored' => true],
            );

            return $action;
        });
    }

    /**
     * @return array{0:ModerationActionType,1:string,2:string}
     */
    private function hideComment(ContentModerationFinding $finding, bool $automated): array
    {
        $comment = ArtworkComment::query()->find($finding->content_id);
        $oldVisibility = $comment && $comment->is_approved ? 'visible' : 'hidden';

        if ($comment) {
            $comment->forceFill(['is_approved' => false])->save();
        }

        return [$automated ? ModerationActionType::AutoHideComment : ModerationActionType::HideComment, $oldVisibility, 'hidden'];
    }

    /**
     * @return array{0:ModerationActionType,1:string,2:string}
     */
    private function hideArtwork(ContentModerationFinding $finding, bool $automated): array
    {
        $artworkId = (int) ($finding->artwork_id ?? $finding->content_id);
        $artwork = Artwork::query()->find($artworkId);
        $oldVisibility = $artwork && $artwork->is_public ? 'visible' : 'hidden';

        if ($artwork) {
            $artwork->forceFill(['is_public' => false])->save();
        }

        return [$automated ? ModerationActionType::AutoHideArtwork : ModerationActionType::HideArtwork, $oldVisibility, 'hidden'];
    }

    /**
     * @return array{0:ModerationActionType,1:string,2:string}
     */
    private function restoreComment(ContentModerationFinding $finding): array
    {
        $comment = ArtworkComment::query()->find($finding->content_id);
        $oldVisibility = $comment && $comment->is_approved ? 'visible' : 'hidden';

        if ($comment) {
            $comment->forceFill(['is_approved' => true])->save();
        }

        return [ModerationActionType::RestoreComment, $oldVisibility, 'visible'];
    }

    /**
     * @return array{0:ModerationActionType,1:string,2:string}
     */
    private function restoreArtwork(ContentModerationFinding $finding): array
    {
        $artworkId = (int) ($finding->artwork_id ?? $finding->content_id);
        $artwork = Artwork::query()->find($artworkId);
        $oldVisibility = $artwork && $artwork->is_public ? 'visible' : 'hidden';

        if ($artwork) {
            $artwork->forceFill(['is_public' => true])->save();
        }

        return [ModerationActionType::RestoreArtwork, $oldVisibility, 'visible'];
    }

    private function updateFinding(
        ContentModerationFinding $finding,
        ModerationStatus $status,
        User $reviewer,
        ?string $notes,
        ModerationActionType $action,
        ?string $oldVisibility = null,
        ?string $newVisibility = null,
    ): void {
        $oldStatus = $finding->status->value;
        $finding->forceFill([
            'status' => $status,
            'reviewed_by' => $reviewer->id,
            'reviewed_at' => \now(),
            'resolved_by' => $reviewer->id,
            'resolved_at' => \now(),
            'action_taken' => $action->value,
            'admin_notes' => $this->normalizeNotes($notes, $finding->admin_notes),
        ])->save();

        $this->actionLogs->log(
            $finding,
            $finding->content_type->value,
            $finding->content_id,
            $action->value,
            $reviewer,
            $oldStatus,
            $status->value,
            $oldVisibility,
            $newVisibility,
            $notes,
        );
    }

    private function normalizeNotes(?string $incoming, ?string $existing): ?string
    {
        $normalized = is_string($incoming) ? trim($incoming) : '';

        return $normalized !== '' ? $normalized : $existing;
    }
}