<?php

namespace App\Http\Controllers;

use App\Events\LeaderboardUpdated;
use App\Events\QuestionStarted;
use App\Events\QuizFinished;
use App\Events\QuizReset;
use App\Events\QuizStarted;
use App\Models\Participant;
use App\Models\Quiz;
use App\Models\QuizAnswer;
use App\Models\QuizQuestion;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;
use Illuminate\Validation\ValidationException;

class AdminQuizController extends Controller
{
    public function login(Request $request)
    {
        if ((bool) $request->session()->get('admin_authenticated', false)) {
            return redirect()->route('admin.quiz');
        }

        return view('admin-login');
    }

    public function authenticate(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'password' => ['required', 'string'],
        ]);

        if (! hash_equals((string) config('app.admin_password'), (string) $validated['password'])) {
            return back()
                ->withErrors(['password' => 'The admin password is incorrect.']);
        }

        $request->session()->put('admin_authenticated', true);
        $request->session()->regenerate();

        return redirect()->intended(route('admin.quiz'));
    }

    public function logout(Request $request): RedirectResponse
    {
        $request->session()->forget('admin_authenticated');
        $request->session()->regenerateToken();

        return redirect()->route('admin.login');
    }

    public function index(Request $request)
    {
        $quizzes = $this->managedQuizzes();
        $quiz = $this->selectedQuiz($request, $quizzes);
        $quiz->load(['questions.options', 'participants']);
        $overall = $this->overallLeaderboard($quiz);
        $shouldShowFinalResult = $this->shouldShowFinalResult($quiz);
        $dashboardStatus = $shouldShowFinalResult ? 'finished' : $quiz->status;

        return view('admin', [
            'quiz' => $quiz,
            'quizzes' => $quizzes,
            'testTypes' => $quizzes->pluck('test_type')->filter()->unique()->values(),
            'participants' => $this->participants($quiz),
            'state' => $this->state($quiz, $dashboardStatus),
            'overall' => $overall,
            'topThree' => $this->dashboardTopThree($quiz, $overall),
            'dashboardStatus' => $dashboardStatus,
            'shouldShowFinalResult' => $shouldShowFinalResult,
            'questionList' => $this->questionList($quiz),
        ]);
    }

    public function report(Request $request, ?Quiz $quiz = null)
    {
        $reportQuizzes = Quiz::query()
            ->withCount(['questions', 'participants'])
            ->orderBy('title')
            ->get();

        $requestedQuiz = $request->query('quiz');
        $isAllReport = $requestedQuiz === 'all';

        if (! $quiz && $requestedQuiz && $requestedQuiz !== 'all') {
            $quiz = Quiz::query()->findOrFail((int) $requestedQuiz);
        }

        if (! $quiz && ! $isAllReport) {
            $quiz = $reportQuizzes->first();
        }

        $quiz?->load(['questions.options', 'participants']);
        $participantReports = $this->participantReportRows($isAllReport ? null : $quiz);
        $overall = collect($participantReports)->take(10)->values()->toArray();

        return view('admin-report', [
            'quiz' => $quiz,
            'reportQuizzes' => $reportQuizzes,
            'isAllReport' => $isAllReport,
            'reportTitle' => $isAllReport ? 'All Tests Result Report' : ($quiz?->title ?? 'Result Report'),
            'reportEyebrow' => $isAllReport ? 'All Tests' : (($quiz?->test_type ?? 'General').' &middot; '.strtoupper($quiz?->status ?? 'waiting')),
            'reportStatus' => $isAllReport ? 'ALL TESTS' : strtoupper($quiz?->status ?? 'waiting'),
            'participantReports' => $participantReports,
            'overall' => $overall,
            'topThree' => $this->reportTopThree($participantReports),
            'questionReports' => $this->questionReports($isAllReport ? null : $quiz),
            'summary' => $this->reportSummary($isAllReport ? null : $quiz),
        ]);
    }

    public function start(): JsonResponse
    {
        $quiz = $this->quiz();

        if (! $quiz->is_enabled) {
            return response()->json([
                'message' => 'Enable this test before starting it.',
            ], 422);
        }

        if ($quiz->status === 'finished') {
            return response()->json([
                'message' => 'Reset this test before starting it again.',
            ], 422);
        }

        if ($this->shouldShowFinalResult($quiz)) {
            return response()->json([
                'message' => 'Reset this test before starting it again.',
            ], 422);
        }

        $firstQuestion = $quiz->questions()->first();

        if (! $firstQuestion) {
            return response()->json([
                'message' => 'Add or import questions before starting this test.',
            ], 422);
        }

        DB::transaction(function () use ($quiz, $firstQuestion) {
            $quiz->answers()->delete();
            $quiz->participants()->update(['score' => 0]);
            $this->activateQuestion($quiz, $firstQuestion);
        });

        $quiz->refresh();
        QuizStarted::dispatch($quiz);
        QuestionStarted::dispatch($quiz, $firstQuestion);

        return response()->json([
            'ok' => true,
            'state' => $this->state($quiz),
        ]);
    }

    public function next(): JsonResponse
    {
        $quiz = $this->quiz();
        $quiz->load('currentQuestion');

        if (! $quiz->is_enabled) {
            return response()->json([
                'message' => 'Enable this test before moving to the next question.',
            ], 422);
        }

        if ($quiz->status === 'finished') {
            return response()->json([
                'message' => 'Reset this test before moving to the next question.',
            ], 422);
        }

        if ($this->shouldShowFinalResult($quiz)) {
            return response()->json([
                'message' => 'Reset this test before moving to the next question.',
            ], 422);
        }

        if (! $quiz->current_question_id) {
            $firstQuestion = $quiz->questions()->first();

            if (! $firstQuestion) {
                return response()->json([
                    'message' => 'Add or import questions before starting this test.',
                ], 422);
            }

            $this->activateQuestion($quiz, $firstQuestion);
            QuestionStarted::dispatch($quiz->refresh(), $firstQuestion);

            return response()->json(['ok' => true, 'state' => $this->state($quiz)]);
        }

        $nextQuestion = $quiz->questions()
            ->where('sort_order', '>', $quiz->currentQuestion->sort_order)
            ->orderBy('sort_order')
            ->first();

        if (! $nextQuestion) {
            $quiz->update([
                'status' => 'finished',
                'current_question_id' => null,
                'current_question_started_at' => null,
                'current_question_ends_at' => null,
            ]);

            $overall = $this->overallLeaderboard($quiz->refresh());
            $topThree = $this->finalTopThree($quiz, $overall);
            QuizFinished::dispatch($quiz, $overall, $topThree);

            return response()->json([
                'ok' => true,
                'finished' => true,
                'overall' => $overall,
                'top_three' => $topThree,
            ]);
        }

        $this->activateQuestion($quiz, $nextQuestion);
        $quiz->refresh();

        QuestionStarted::dispatch($quiz, $nextQuestion);
        LeaderboardUpdated::dispatch($quiz, $nextQuestion, [], $this->overallLeaderboard($quiz));

        return response()->json([
            'ok' => true,
            'state' => $this->state($quiz),
        ]);
    }

    public function reset(): JsonResponse
    {
        $quiz = $this->quiz();

        DB::transaction(function () use ($quiz) {
            QuizAnswer::where('quiz_id', $quiz->id)->delete();
            $quiz->participants()->delete();
            $quiz->update([
                'status' => 'waiting',
                'current_question_id' => null,
                'current_question_started_at' => null,
                'current_question_ends_at' => null,
            ]);
        });

        $quiz->refresh();
        QuizReset::dispatch($quiz);

        return response()->json(['ok' => true]);
    }

    public function import(Request $request): RedirectResponse
    {
        $request->validate([
            'questions_import' => ['nullable', 'string', 'max:50000'],
            'questions_file' => ['nullable', 'file', 'mimes:csv,txt', 'max:1024'],
            'replace_existing' => ['nullable', 'boolean'],
            'quiz_id' => ['nullable', 'integer', 'exists:quizzes,id'],
        ]);

        $source = trim((string) $request->input('questions_import', ''));

        if ($request->hasFile('questions_file')) {
            $source = trim((string) file_get_contents($request->file('questions_file')->getRealPath()));
        }

        if ($source === '') {
            throw ValidationException::withMessages([
                'questions_import' => 'Paste questions or upload a CSV/TXT file.',
            ]);
        }

        $questions = $this->parseQuestionImport($source);
        $quiz = $this->quiz();
        $replaceExisting = $request->boolean('replace_existing');

        DB::transaction(function () use ($quiz, $questions, $replaceExisting) {
            $nextSortOrder = (int) $quiz->questions()->max('sort_order') + 1;

            if ($replaceExisting) {
                QuizAnswer::where('quiz_id', $quiz->id)->delete();
                $quiz->questions()->delete();
                $quiz->participants()->update(['score' => 0]);
                $nextSortOrder = 1;
            }

            $quiz->update([
                'status' => 'waiting',
                'current_question_id' => null,
                'current_question_started_at' => null,
                'current_question_ends_at' => null,
            ]);

            foreach ($questions as $questionIndex => $item) {
                $question = $quiz->questions()->create([
                    'sort_order' => $nextSortOrder + $questionIndex,
                    'question_text' => $item['question'],
                    'duration_seconds' => $item['duration_seconds'],
                ]);

                foreach ($item['options'] as $optionIndex => $option) {
                    $question->options()->create([
                        'sort_order' => $optionIndex + 1,
                        'option_text' => $option['text'],
                        'is_correct' => $option['is_correct'],
                    ]);
                }
            }
        });

        return redirect()
            ->route('admin.quiz', ['quiz' => $quiz->id])
            ->with('import_success', count($questions).' questions imported.');
    }

    public function storeTest(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'title' => ['required', 'string', 'max:160'],
            'description' => ['nullable', 'string', 'max:1000'],
            'test_type' => ['nullable', 'string', 'max:80'],
            'is_enabled' => ['nullable', 'boolean'],
        ]);

        $quiz = Quiz::create([
            'uuid' => (string) Str::uuid(),
            'title' => trim($validated['title']),
            'description' => trim((string) ($validated['description'] ?? '')),
            'slug' => $this->uniqueSlug($validated['title']),
            'test_type' => trim((string) ($validated['test_type'] ?? 'General')) ?: 'General',
            'status' => 'waiting',
            'is_enabled' => $request->boolean('is_enabled', true),
            'current_question_id' => null,
            'current_question_started_at' => null,
            'current_question_ends_at' => null,
        ]);

        return redirect()
            ->route('admin.quiz', ['quiz' => $quiz->id])
            ->with('test_success', 'Test created. Import questions to prepare it.');
    }

    public function storeQuestion(Request $request): RedirectResponse
    {
        $validated = $request->validate([
            'quiz_id' => ['required', 'integer', 'exists:quizzes,id'],
            'question_text' => ['required', 'string', 'max:2000'],
            'option_a' => ['required', 'string', 'max:255'],
            'option_b' => ['required', 'string', 'max:255'],
            'option_c' => ['required', 'string', 'max:255'],
            'option_d' => ['required', 'string', 'max:255'],
            'correct_option' => ['required', 'in:A,B,C,D'],
            'duration_seconds' => ['required', 'integer', 'min:5', 'max:300'],
        ]);

        $quiz = Quiz::query()->findOrFail((int) $validated['quiz_id']);

        DB::transaction(function () use ($quiz, $validated) {
            $question = $quiz->questions()->create([
                'sort_order' => ((int) $quiz->questions()->max('sort_order')) + 1,
                'question_text' => trim($validated['question_text']),
                'duration_seconds' => (int) $validated['duration_seconds'],
            ]);

            foreach (['A', 'B', 'C', 'D'] as $index => $letter) {
                $field = 'option_'.strtolower($letter);

                $question->options()->create([
                    'sort_order' => $index + 1,
                    'option_text' => trim($validated[$field]),
                    'is_correct' => $validated['correct_option'] === $letter,
                ]);
            }

            $quiz->update([
                'status' => 'waiting',
                'current_question_id' => null,
                'current_question_started_at' => null,
                'current_question_ends_at' => null,
            ]);
        });

        return redirect()
            ->route('admin.quiz', ['quiz' => $quiz->id])
            ->with('test_success', 'Question added.');
    }

    public function toggleTest(Quiz $quiz): RedirectResponse
    {
        $isEnabled = ! $quiz->is_enabled;

        $quiz->update([
            'is_enabled' => $isEnabled,
        ]);

        return redirect()
            ->route('admin.quiz', ['quiz' => $quiz->id])
            ->with('test_success', $quiz->title.' is now '.($isEnabled ? 'active' : 'inactive').'.');
    }

    public function destroyTest(Quiz $quiz): RedirectResponse
    {
        $quizTitle = $quiz->title;
        $quiz->delete();

        $nextQuiz = Quiz::query()->orderByDesc('is_enabled')->orderBy('title')->first();

        return redirect()
            ->route('admin.quiz', $nextQuiz ? ['quiz' => $nextQuiz->id] : [])
            ->with('test_success', $quizTitle.' deleted.');
    }

    public function downloadTemplate()
    {
        return response()->download(
            public_path('templates/questions_answers_template.csv'),
            'questions_answers_template.csv',
            ['Content-Type' => 'text/csv']
        );
    }

    private function parseQuestionImport(string $source): array
    {
        $questions = [];
        $lines = preg_split('/\R/', trim($source)) ?: [];

        foreach ($lines as $lineIndex => $line) {
            $line = trim($line);

            if ($line === '' || str_starts_with($line, '#')) {
                continue;
            }

            $delimiter = str_contains($line, '|') ? '|' : ',';
            $columns = array_values(array_filter(
                array_map('trim', str_getcsv($line, $delimiter)),
                fn (string $value) => $value !== ''
            ));

            if ($this->isImportHeader($columns)) {
                continue;
            }

            if (count($columns) >= 6 && preg_match('/^[A-D]$/i', (string) $columns[5])) {
                $questions[] = $this->parseTemplateQuestionRow($columns, $lineIndex);
                continue;
            }

            if (count($columns) < 3) {
                $this->throwImportError($lineIndex, 'Each row needs a question and at least two answers.');
            }

            $questionText = array_shift($columns);
            $durationSeconds = 20;
            $correctColumn = null;

            foreach ($columns as $columnIndex => $column) {
                if (preg_match('/^(duration|seconds|time)\s*[:=]\s*(\d+)$/i', $column, $matches)) {
                    $durationSeconds = (int) $matches[2];
                    unset($columns[$columnIndex]);
                    continue;
                }

                if (preg_match('/^correct\s*[:=]\s*(\d+)$/i', $column, $matches)) {
                    $correctColumn = (int) $matches[1] - 1;
                    unset($columns[$columnIndex]);
                }
            }

            $columns = array_values($columns);
            $lastColumn = end($columns);

            if (count($columns) >= 4 && is_string($lastColumn) && preg_match('/^\d+$/', $lastColumn)) {
                $durationSeconds = (int) array_pop($columns);
            }

            $questionText = trim((string) $questionText);

            if ($questionText === '') {
                $this->throwImportError($lineIndex, 'Question text is required.');
            }

            if ($durationSeconds < 5 || $durationSeconds > 300) {
                $this->throwImportError($lineIndex, 'Duration must be between 5 and 300 seconds.');
            }

            if (count($columns) < 2) {
                $this->throwImportError($lineIndex, 'Each question needs at least two answers.');
            }

            $options = [];

            foreach ($columns as $optionIndex => $optionText) {
                $optionText = trim((string) $optionText);
                $isCorrect = false;

                if (str_starts_with($optionText, '*') || str_ends_with($optionText, '*')) {
                    $isCorrect = true;
                    $optionText = trim($optionText, "* \t\n\r\0\x0B");
                }

                if ($correctColumn === $optionIndex) {
                    $isCorrect = true;
                }

                if ($optionText === '') {
                    $this->throwImportError($lineIndex, 'Answer text cannot be empty.');
                }

                if (strlen($optionText) > 255) {
                    $this->throwImportError($lineIndex, 'Answer text must be 255 characters or fewer.');
                }

                $options[] = [
                    'text' => $optionText,
                    'is_correct' => $isCorrect,
                ];
            }

            $correctCount = count(array_filter($options, fn (array $option) => $option['is_correct']));

            if ($correctCount === 0) {
                $options[0]['is_correct'] = true;
            } elseif ($correctCount > 1) {
                $this->throwImportError($lineIndex, 'Only one answer can be marked correct.');
            }

            $questions[] = [
                'question' => $questionText,
                'duration_seconds' => $durationSeconds,
                'options' => $options,
            ];
        }

        if ($questions === []) {
            throw ValidationException::withMessages([
                'questions_import' => 'No importable questions were found.',
            ]);
        }

        return $questions;
    }

    private function isImportHeader(array $columns): bool
    {
        $first = strtolower($columns[0] ?? '');
        $joined = strtolower(implode('|', $columns));

        return str_contains($first, 'question')
            && (str_contains($joined, 'option') || str_contains($joined, 'answer'));
    }

    private function parseTemplateQuestionRow(array $columns, int $lineIndex): array
    {
        $questionText = trim((string) ($columns[0] ?? ''));
        $durationSeconds = isset($columns[6]) && preg_match('/^\d+$/', (string) $columns[6])
            ? (int) $columns[6]
            : 20;

        if ($questionText === '') {
            $this->throwImportError($lineIndex, 'Question text is required.');
        }

        if ($durationSeconds < 5 || $durationSeconds > 300) {
            $this->throwImportError($lineIndex, 'Duration must be between 5 and 300 seconds.');
        }

        $correctLetter = strtoupper(trim((string) $columns[5]));
        $options = [];

        foreach (['A', 'B', 'C', 'D'] as $index => $letter) {
            $optionText = trim((string) ($columns[$index + 1] ?? ''));
            $optionText = trim($optionText, "* \t\n\r\0\x0B");

            if ($optionText === '') {
                $this->throwImportError($lineIndex, 'All four option columns are required.');
            }

            $options[] = [
                'text' => $optionText,
                'is_correct' => $correctLetter === $letter,
            ];
        }

        return [
            'question' => $questionText,
            'duration_seconds' => $durationSeconds,
            'options' => $options,
        ];
    }

    private function throwImportError(int $lineIndex, string $message): void
    {
        throw ValidationException::withMessages([
            'questions_import' => 'Line '.($lineIndex + 1).': '.$message,
        ]);
    }

    private function activateQuestion(Quiz $quiz, QuizQuestion $question): void
    {
        $now = now();
        $quiz->update([
            'status' => 'active',
            'current_question_id' => $question->id,
            'current_question_started_at' => $now,
            'current_question_ends_at' => $now->copy()->addSeconds($question->duration_seconds),
        ]);
    }

    private function quiz(): Quiz
    {
        $quizId = request('quiz_id') ?: request('quiz');

        if ($quizId) {
            return Quiz::query()->findOrFail((int) $quizId);
        }

        return Quiz::query()->where('slug', 'main')->firstOrFail();
    }

    private function managedQuizzes()
    {
        if (! Quiz::query()->exists()) {
            Quiz::create([
                'uuid' => (string) Str::uuid(),
                'title' => 'Main Quiz',
                'slug' => 'main',
                'test_type' => 'General',
                'status' => 'waiting',
                'is_enabled' => true,
            ]);
        }

        return Quiz::query()
            ->withCount(['questions', 'participants'])
            ->orderByDesc('is_enabled')
            ->orderBy('title')
            ->get();
    }

    private function selectedQuiz(Request $request, $quizzes): Quiz
    {
        $requestedQuizId = (int) $request->query('quiz');

        return $quizzes->firstWhere('id', $requestedQuizId)
            ?? $quizzes->firstWhere('slug', 'main')
            ?? $quizzes->first();
    }

    private function uniqueSlug(string $title): string
    {
        $baseSlug = Str::slug($title) ?: 'test';
        $slug = $baseSlug;
        $counter = 2;

        while (Quiz::query()->where('slug', $slug)->exists()) {
            $slug = $baseSlug.'-'.$counter;
            $counter++;
        }

        return $slug;
    }

    private function participants(Quiz $quiz): array
    {
        return $quiz->participants()
            ->select(['id', 'name', 'session_uuid', 'score', 'created_at'])
            ->oldest()
            ->get()
            ->map(fn ($participant) => [
                'id' => $participant->id,
                'name' => $participant->name,
                'session_uuid' => $participant->session_uuid,
                'score' => $participant->score,
                'joined_at' => optional($participant->created_at)->toIso8601String(),
            ])
            ->values()
            ->toArray();
    }

    private function state(Quiz $quiz, ?string $status = null): array
    {
        $quiz->loadMissing(['currentQuestion.options']);

        return [
            'uuid' => $quiz->uuid,
            'title' => $quiz->title,
            'status' => $status ?? $quiz->status,
            'current_question_id' => $quiz->current_question_id,
            'current_question_started_at' => optional($quiz->current_question_started_at)->toIso8601String(),
            'current_question_ends_at' => optional($quiz->current_question_ends_at)->toIso8601String(),
            'current_question' => $quiz->currentQuestion ? [
                'id' => $quiz->currentQuestion->id,
                'sort_order' => $quiz->currentQuestion->sort_order,
                'text' => $quiz->currentQuestion->question_text,
                'duration_seconds' => $quiz->currentQuestion->duration_seconds,
                'options' => $quiz->currentQuestion->options->map(fn ($option) => [
                    'id' => $option->id,
                    'text' => $option->option_text,
                    'sort_order' => $option->sort_order,
                ])->values(),
            ] : null,
        ];
    }

    private function currentQuestionTopThree(Quiz $quiz): array
    {
        if (! $quiz->current_question_id) {
            return [];
        }

        return $this->topThreeForQuestion($quiz, $quiz->current_question_id);
    }

    private function topThreeForQuestion(Quiz $quiz, int $questionId): array
    {
        return QuizAnswer::query()
            ->with('participant')
            ->where('quiz_id', $quiz->id)
            ->where('quiz_question_id', $questionId)
            ->where('is_correct', true)
            ->orderBy('response_ms')
            ->limit(3)
            ->get()
            ->map(fn ($answer, $index) => [
                'rank' => $index + 1,
                'name' => $answer->participant->name,
                'response_ms' => $answer->response_ms,
                'score_awarded' => $answer->score_awarded,
                'total_score' => $answer->participant->score,
            ])
            ->values()
            ->toArray();
    }

    private function dashboardTopThree(Quiz $quiz, array $overall): array
    {
        if ($this->shouldShowFinalResult($quiz)) {
            return $this->finalTopThree($quiz, $overall);
        }

        return $this->currentQuestionTopThree($quiz);
    }

    private function shouldShowFinalResult(Quiz $quiz): bool
    {
        return $quiz->status === 'finished'
            || (! $quiz->current_question_id && $this->hasResultSnapshot($quiz));
    }

    private function hasResultSnapshot(Quiz $quiz): bool
    {
        return $quiz->answers()->exists();
    }

    private function finalTopThree(Quiz $quiz, ?array $overall = null): array
    {
        $overall ??= $this->overallLeaderboard($quiz);

        return collect($overall)
            ->take(3)
            ->map(fn (array $item) => [
                'rank' => $item['rank'],
                'name' => $item['name'],
                'score' => $item['score'],
                'total_score' => $item['score'],
            ])
            ->values()
            ->toArray();
    }

    private function overallLeaderboard(Quiz $quiz): array
    {
        return $quiz->participants()
            ->orderByDesc('score')
            ->orderBy('created_at')
            ->limit(10)
            ->get()
            ->map(fn ($participant, $index) => [
                'rank' => $index + 1,
                'name' => $participant->name,
                'score' => $participant->score,
            ])
            ->values()
            ->toArray();
    }

    private function questionList(Quiz $quiz): array
    {
        return $quiz->questions()
            ->with('options')
            ->get()
            ->map(fn (QuizQuestion $question) => [
                'id' => $question->id,
                'sort_order' => $question->sort_order,
                'text' => $question->question_text,
                'duration_seconds' => $question->duration_seconds,
                'correct_option' => optional($question->options->firstWhere('is_correct', true))->sort_order,
                'options' => $question->options->map(fn ($option) => [
                    'sort_order' => $option->sort_order,
                    'text' => $option->option_text,
                    'is_correct' => $option->is_correct,
                ])->values(),
            ])
            ->values()
            ->toArray();
    }

    private function reportSummary(?Quiz $quiz = null): array
    {
        $answerQuery = QuizAnswer::query();
        $correctQuery = QuizAnswer::query()->where('is_correct', true);

        if ($quiz) {
            $answerQuery->where('quiz_id', $quiz->id);
            $correctQuery->where('quiz_id', $quiz->id);
        }

        $answerCount = $answerQuery->count();
        $correctCount = $correctQuery->count();
        $questionsCount = $quiz
            ? $quiz->questions()->count()
            : QuizQuestion::query()->count();
        $participantsCount = $quiz
            ? $quiz->participants()->count()
            : Participant::query()->count();
        $averageScore = $quiz
            ? $quiz->participants()->avg('score')
            : Participant::query()->avg('score');

        return [
            'tests' => $quiz ? 1 : Quiz::query()->count(),
            'questions' => $questionsCount,
            'participants' => $participantsCount,
            'answers' => $answerCount,
            'correct_answers' => $correctCount,
            'accuracy' => $answerCount > 0 ? round(($correctCount / $answerCount) * 100) : 0,
            'average_score' => round((float) $averageScore),
        ];
    }

    private function questionReports(?Quiz $quiz = null): array
    {
        $answerQuery = QuizAnswer::query()
            ->with(['participant', 'option'])
            ->when($quiz, fn ($query) => $query->where('quiz_id', $quiz->id));
        $answers = $answerQuery->get()->groupBy('quiz_question_id');

        return QuizQuestion::query()
            ->with(['quiz', 'options'])
            ->when($quiz, fn ($query) => $query->where('quiz_id', $quiz->id))
            ->orderBy('quiz_id')
            ->orderBy('sort_order')
            ->get()
            ->map(function (QuizQuestion $question) use ($answers) {
                $questionAnswers = $answers->get($question->id, collect());
                $answerCount = $questionAnswers->count();
                $correctAnswers = $questionAnswers->where('is_correct', true);
                $correctOption = $question->options->firstWhere('is_correct', true);

                return [
                    'id' => $question->id,
                    'test_title' => $question->quiz?->title ?? 'Test',
                    'sort_order' => $question->sort_order,
                    'text' => $question->question_text,
                    'duration_seconds' => $question->duration_seconds,
                    'answer_count' => $answerCount,
                    'correct_count' => $correctAnswers->count(),
                    'accuracy' => $answerCount > 0 ? round(($correctAnswers->count() / $answerCount) * 100) : 0,
                    'correct_letter' => $correctOption ? chr(64 + (int) $correctOption->sort_order) : '-',
                    'correct_text' => $correctOption?->option_text ?? '-',
                    'options' => $question->options->map(fn ($option) => [
                        'letter' => chr(64 + (int) $option->sort_order),
                        'text' => $option->option_text,
                        'is_correct' => $option->is_correct,
                        'count' => $questionAnswers->where('quiz_option_id', $option->id)->count(),
                    ])->values()->toArray(),
                    'fastest' => $correctAnswers
                        ->sortBy('response_ms')
                        ->take(3)
                        ->values()
                        ->map(fn ($answer, $index) => [
                            'rank' => $index + 1,
                            'name' => $answer->participant?->name ?? 'Participant',
                            'response_ms' => $answer->response_ms,
                            'score_awarded' => $answer->score_awarded,
                        ])
                        ->toArray(),
                ];
            })
            ->values()
            ->toArray();
    }

    private function participantReportRows(?Quiz $quiz = null): array
    {
        $quizzes = Quiz::query()
            ->withCount('questions')
            ->when($quiz, fn ($query) => $query->whereKey($quiz->id))
            ->with(['participants' => fn ($query) => $query->orderByDesc('score')->orderBy('created_at')])
            ->orderBy('title')
            ->get();

        $rows = collect();

        foreach ($quizzes as $reportQuiz) {
            $answerStats = QuizAnswer::query()
                ->select([
                    'participant_id',
                    DB::raw('COUNT(*) as answered_count'),
                    DB::raw('SUM(CASE WHEN is_correct = 1 THEN 1 ELSE 0 END) as correct_count'),
                ])
                ->where('quiz_id', $reportQuiz->id)
                ->groupBy('participant_id')
                ->get()
                ->keyBy('participant_id');

            foreach ($reportQuiz->participants as $participant) {
                $stats = $answerStats->get($participant->id);
                $totalQuestions = (int) $reportQuiz->questions_count;
                $correctCount = (int) ($stats->correct_count ?? 0);
                $answeredCount = (int) ($stats->answered_count ?? 0);

                $rows->push([
                    'participant_id' => $participant->id,
                    'quiz_id' => $reportQuiz->id,
                    'test_title' => $reportQuiz->title,
                    'test_type' => $reportQuiz->test_type,
                    'name' => $participant->name,
                    'score' => (int) $participant->score,
                    'answered_count' => $answeredCount,
                    'correct_count' => $correctCount,
                    'total_questions' => $totalQuestions,
                    'correct_ratio' => $correctCount.'/'.$totalQuestions,
                    'accuracy' => $totalQuestions > 0 ? round(($correctCount / $totalQuestions) * 100) : 0,
                    'joined_at' => optional($participant->created_at)->toIso8601String(),
                ]);
            }
        }

        return $rows
            ->sortBy([
                ['score', 'desc'],
                ['correct_count', 'desc'],
                ['name', 'asc'],
            ])
            ->values()
            ->map(fn (array $row, int $index) => ['rank' => $index + 1] + $row)
            ->toArray();
    }

    private function reportTopThree(array $participantReports): array
    {
        return collect($participantReports)
            ->take(3)
            ->map(fn (array $item, int $index) => [
                'rank' => $index + 1,
                'name' => $item['name'],
                'score' => $item['score'],
                'total_score' => $item['score'],
                'correct_ratio' => $item['correct_ratio'],
                'test_title' => $item['test_title'],
            ])
            ->values()
            ->toArray();
    }
}
