<?php

namespace App\Http\Controllers\Api\Admin;

use App\Http\Controllers\Controller;
use App\Models\NovaCard;
use App\Models\Report;
use App\Models\ReportHistory;
use App\Services\NovaCards\NovaCardPublishModerationService;
use App\Support\Moderation\ReportTargetResolver;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;

final class ModerationReportQueueController extends Controller
{
    public function __construct(
        private readonly ReportTargetResolver $targets,
        private readonly NovaCardPublishModerationService $moderation,
    ) {}

    public function index(Request $request): JsonResponse
    {
        $status = (string) $request->query('status', 'open');
        $status = in_array($status, ['open', 'reviewing', 'closed'], true) ? $status : 'open';
        $group = (string) $request->query('group', '');

        $query = Report::query()
            ->with(['reporter:id,username', 'lastModeratedBy:id,username', 'historyEntries.actor:id,username'])
            ->where('status', $status)
            ->orderByDesc('id');

        if ($group === 'nova_cards') {
            $query->whereIn('target_type', $this->targets->novaCardTargetTypes());
        }

        $items = $query->paginate(30);

        return response()->json([
            'data' => collect($items->items())
                ->map(fn (Report $report): array => $this->serializeReport($report))
                ->values()
                ->all(),
            'meta' => [
                'current_page' => $items->currentPage(),
                'last_page' => $items->lastPage(),
                'per_page' => $items->perPage(),
                'total' => $items->total(),
                'from' => $items->firstItem(),
                'to' => $items->lastItem(),
            ],
        ]);
    }

    public function update(Request $request, Report $report): JsonResponse
    {
        $data = $request->validate([
            'status' => 'sometimes|in:open,reviewing,closed',
            'moderator_note' => 'sometimes|nullable|string|max:2000',
        ]);

        $before = [];
        $after = [];
        $user = $request->user();

        DB::transaction(function () use ($data, $report, $user, &$before, &$after): void {
            if (array_key_exists('status', $data) && $data['status'] !== $report->status) {
                $before['status'] = (string) $report->status;
                $after['status'] = (string) $data['status'];
                $report->status = $data['status'];
            }

            if (array_key_exists('moderator_note', $data)) {
                $normalizedNote = is_string($data['moderator_note']) ? trim($data['moderator_note']) : null;
                $normalizedNote = $normalizedNote !== '' ? $normalizedNote : null;

                if ($normalizedNote !== $report->moderator_note) {
                    $before['moderator_note'] = $report->moderator_note;
                    $after['moderator_note'] = $normalizedNote;
                    $report->moderator_note = $normalizedNote;
                }
            }

            if ($before !== [] || $after !== []) {
                $report->last_moderated_by_id = $user?->id;
                $report->last_moderated_at = now();
                $report->save();

                $report->historyEntries()->create([
                    'actor_user_id' => $user?->id,
                    'action_type' => 'report_updated',
                    'summary' => $this->buildUpdateSummary($before, $after),
                    'note' => $report->moderator_note,
                    'before_json' => $before !== [] ? $before : null,
                    'after_json' => $after !== [] ? $after : null,
                    'created_at' => now(),
                ]);
            }
        });

        $report = $report->fresh(['reporter:id,username', 'lastModeratedBy:id,username', 'historyEntries.actor:id,username']);

        return response()->json([
            'report' => $this->serializeReport($report),
        ]);
    }

    public function moderateTarget(Request $request, Report $report): JsonResponse
    {
        $data = $request->validate([
            'action' => 'required|in:approve_card,flag_card,reject_card',
            'disposition' => 'nullable|in:' . implode(',', array_keys(NovaCardPublishModerationService::DISPOSITION_LABELS)),
        ]);

        $card = $this->targets->resolveModerationCard($report);
        abort_unless($card !== null, 422, 'This report does not have a Nova Card moderation target.');

        DB::transaction(function () use ($card, $data, $report, $request): void {
            $before = [
                'card_id' => (int) $card->id,
                'moderation_status' => (string) $card->moderation_status,
            ];

            $nextStatus = match ($data['action']) {
                'approve_card' => NovaCard::MOD_APPROVED,
                'flag_card' => NovaCard::MOD_FLAGGED,
                'reject_card' => NovaCard::MOD_REJECTED,
            };
            $card = $this->moderation->recordStaffOverride(
                $card,
                $nextStatus,
                $request->user(),
                'report_queue',
                [
                    'note' => $report->moderator_note,
                    'report_id' => $report->id,
                    'disposition' => $data['disposition'] ?? null,
                ],
            );

            $report->last_moderated_by_id = $request->user()?->id;
            $report->last_moderated_at = now();
            $report->save();

            $report->historyEntries()->create([
                'actor_user_id' => $request->user()?->id,
                'action_type' => 'target_moderated',
                'summary' => $this->buildTargetModerationSummary($data['action'], $card),
                'note' => $report->moderator_note,
                'before_json' => $before,
                'after_json' => [
                    'card_id' => (int) $card->id,
                    'moderation_status' => (string) $card->moderation_status,
                    'action' => (string) $data['action'],
                ],
                'created_at' => now(),
            ]);
        });

        $report = $report->fresh(['reporter:id,username', 'lastModeratedBy:id,username', 'historyEntries.actor:id,username']);

        return response()->json([
            'report' => $this->serializeReport($report),
        ]);
    }

    private function buildUpdateSummary(array $before, array $after): string
    {
        $parts = [];

        if (array_key_exists('status', $after)) {
            $parts[] = sprintf('Status %s -> %s', $before['status'], $after['status']);
        }

        if (array_key_exists('moderator_note', $after)) {
            $parts[] = $after['moderator_note'] ? 'Moderator note updated' : 'Moderator note cleared';
        }

        return $parts !== [] ? implode(' • ', $parts) : 'Report reviewed';
    }

    private function buildTargetModerationSummary(string $action, NovaCard $card): string
    {
        return match ($action) {
            'approve_card' => sprintf('Approved card #%d', $card->id),
            'flag_card' => sprintf('Flagged card #%d', $card->id),
            'reject_card' => sprintf('Rejected card #%d', $card->id),
            default => sprintf('Updated card #%d', $card->id),
        };
    }

    private function serializeReport(Report $report): array
    {
        return [
            'id' => (int) $report->id,
            'status' => (string) $report->status,
            'target_type' => (string) $report->target_type,
            'target_id' => (int) $report->target_id,
            'reason' => (string) $report->reason,
            'details' => $report->details,
            'moderator_note' => $report->moderator_note,
            'created_at' => optional($report->created_at)?->toISOString(),
            'updated_at' => optional($report->updated_at)?->toISOString(),
            'last_moderated_at' => optional($report->last_moderated_at)?->toISOString(),
            'reporter' => $report->reporter ? [
                'id' => (int) $report->reporter->id,
                'username' => (string) $report->reporter->username,
            ] : null,
            'last_moderated_by' => $report->lastModeratedBy ? [
                'id' => (int) $report->lastModeratedBy->id,
                'username' => (string) $report->lastModeratedBy->username,
            ] : null,
            'target' => $this->targets->summarize($report),
            'history' => $report->historyEntries
                ->take(8)
                ->map(fn (ReportHistory $entry): array => [
                    'id' => (int) $entry->id,
                    'action_type' => (string) $entry->action_type,
                    'summary' => $entry->summary,
                    'note' => $entry->note,
                    'before' => $entry->before_json,
                    'after' => $entry->after_json,
                    'created_at' => optional($entry->created_at)?->toISOString(),
                    'actor' => $entry->actor ? [
                        'id' => (int) $entry->actor->id,
                        'username' => (string) $entry->actor->username,
                    ] : null,
                ])
                ->values()
                ->all(),
        ];
    }
}
