<?php

declare(strict_types=1);

namespace App\Http\Controllers\Api\App;

use App\Http\Controllers\Controller;
use App\Models\FavoriteQuestion;
use App\Models\Paper;
use App\Models\QuestionBank;
use App\Models\QuizAttempt;
use App\Models\User;
use App\Models\WrongQuestion;
use App\Services\LearningAccessService;
use App\Services\QuizService;
use App\Support\ApiResponse;
use hg\apidoc\annotation as Apidoc;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Tymon\JWTAuth\Facades\JWTAuth;

#[Apidoc\Group('用户端')]
#[Apidoc\Title('做题')]
#[Apidoc\RouteMiddleware(['jwt.auth'])]
final class QuizController extends Controller
{
    #[Apidoc\Title('开始背题/刷题/抽题')]
    #[Apidoc\Url('/app/banks/{bank}/attempts')]
    #[Apidoc\Method('POST')]
    public function startBank(Request $request, mixed $bank, QuizService $service, LearningAccessService $access): JsonResponse
    {
        $bank = $this->resolveBank($bank);
        abort_if(! $access->canAccessBank($this->currentUser($request), $bank), 403);

        $data = $request->validate([
            'mode' => ['required', 'in:memorize,wrong_memorize,practice,wrong_practice,sequence,random,wrong_random'],
            'category_id' => ['nullable', 'exists:question_categories,id'],
            'type' => ['nullable'],
            'type.*' => ['in:single,multiple,judge,blank'],
            'tag_ids' => ['nullable', 'array'],
            'tag_ids.*' => ['integer', 'exists:question_tags,id'],
            'limit' => ['nullable', 'integer', 'min:1', 'max:100'],
        ]);

        return ApiResponse::success($service->startPractice($this->currentUser($request), $bank, $data['mode'], $data), '已开始');
    }

    #[Apidoc\Title('题库标签')]
    #[Apidoc\Url('/app/banks/{bank}/tags')]
    #[Apidoc\Method('GET')]
    public function tags(Request $request, mixed $bank, LearningAccessService $access): JsonResponse
    {
        $bank = $this->resolveBank($bank);
        abort_if(! $access->canAccessBank($this->currentUser($request), $bank), 403);

        return ApiResponse::success($bank->tags()->orderBy('name')->get());
    }

    #[Apidoc\Title('题库历史练习记录')]
    #[Apidoc\Url('/app/banks/{bank}/attempts/history')]
    #[Apidoc\Method('GET')]
    public function bankAttemptHistory(Request $request, mixed $bank, LearningAccessService $access): JsonResponse
    {
        $bank = $this->resolveBank($bank);
        $user = $this->currentUser($request);
        abort_if(! $access->canAccessBank($user, $bank), 403);

        return ApiResponse::page(
            QuizAttempt::query()
                ->where('user_id', $user->id)
                ->where('question_bank_id', $bank->id)
                ->whereIn('mode', ['sequence', 'random'])
                ->where('status', 'submitted')
                ->latest('submitted_at')
                ->paginate((int) $request->query('per_page', 10), ['id', 'mode', 'status', 'total_questions', 'correct_count', 'score', 'started_at', 'submitted_at']),
        );
    }

    #[Apidoc\Title('开始整卷测试')]
    #[Apidoc\Url('/app/papers/{paper}/attempts')]
    #[Apidoc\Method('POST')]
    public function startPaper(Request $request, mixed $paper, QuizService $service, LearningAccessService $access): JsonResponse
    {
        $paper = $this->resolvePaper($paper);
        abort_if(! $access->canAccessPaper($this->currentUser($request), $paper), 403);

        return ApiResponse::success($service->startPaper($this->currentUser($request), $paper), '已开始');
    }

    #[Apidoc\Title('继续作答')]
    #[Apidoc\Url('/app/attempts/{attempt}')]
    #[Apidoc\Method('GET')]
    public function show(Request $request, mixed $attempt): JsonResponse
    {
        $attempt = $this->resolveAttempt($attempt);
        $user = $this->currentUser($request);
        abort_if($attempt->user_id !== $user->id, 403);

        $attempt->load('items.question.options');

        if (str_contains((string) $attempt->mode, 'wrong')) {
            $wrongIds = WrongQuestion::query()
                ->where('user_id', $user->id)
                ->whereNull('mastered_at')
                ->whereIn('question_id', $attempt->items->pluck('question_id'))
                ->pluck('id', 'question_id');

            $attempt->items->each(fn ($item) => $item->setAttribute('wrong_question_id', $wrongIds[$item->question_id] ?? null));
        }

        return ApiResponse::success($attempt);
    }

    #[Apidoc\Title('提交单题答案')]
    #[Apidoc\Url('/app/attempts/{attempt}/answer')]
    #[Apidoc\Method('POST')]
    public function answer(Request $request, mixed $attempt, QuizService $service): JsonResponse
    {
        $attempt = $this->resolveAttempt($attempt);
        $data = $request->validate([
            'question_id' => ['required', 'exists:questions,id'],
            'answer' => ['array'],
            'duration_seconds' => ['nullable', 'integer', 'min:0'],
        ]);

        return ApiResponse::success(
            $service->answer($this->currentUser($request), $attempt, (int) $data['question_id'], $data['answer'] ?? [], (int) ($data['duration_seconds'] ?? 0)),
            '已作答',
        );
    }

    #[Apidoc\Title('保存作答位置')]
    #[Apidoc\Url('/app/attempts/{attempt}/position')]
    #[Apidoc\Method('PUT')]
    public function updatePosition(Request $request, mixed $attempt): JsonResponse
    {
        $attempt = $this->resolveAttempt($attempt);
        $user = $this->currentUser($request);
        abort_if($attempt->user_id !== $user->id, 403);

        $data = $request->validate([
            'current_index' => ['required', 'integer', 'min:0'],
        ]);

        $maxIndex = max(0, $attempt->total_questions - 1);
        $attempt->update([
            'current_index' => min((int) $data['current_index'], $maxIndex),
        ]);

        return ApiResponse::success($attempt->fresh(), '位置已保存');
    }

    #[Apidoc\Title('交卷')]
    #[Apidoc\Url('/app/attempts/{attempt}/submit')]
    #[Apidoc\Method('POST')]
    public function submit(Request $request, mixed $attempt, QuizService $service): JsonResponse
    {
        $attempt = $this->resolveAttempt($attempt);

        return ApiResponse::success($service->submit($this->currentUser($request), $attempt), '已交卷');
    }

    #[Apidoc\Title('错题列表')]
    #[Apidoc\Url('/app/wrong-questions')]
    #[Apidoc\Method('GET')]
    public function wrongQuestions(Request $request): JsonResponse
    {
        return ApiResponse::page(
            WrongQuestion::query()
                ->where('user_id', $this->currentUser($request)->id)
                ->whereNull('mastered_at')
                ->when($request->query('question_bank_id'), fn ($query, $bankId) => $query->whereHas(
                    'question',
                    fn ($questionQuery) => $questionQuery->where('question_bank_id', $bankId),
                ))
                ->with('question.options')
                ->latest()
                ->paginate((int) $request->query('per_page', 20)),
        );
    }

    #[Apidoc\Title('标记错题已掌握')]
    #[Apidoc\Url('/app/wrong-questions/{wrongQuestion}/mastered')]
    #[Apidoc\Method('POST')]
    public function masterWrongQuestion(Request $request, mixed $wrongQuestion): JsonResponse
    {
        $wrongQuestion = WrongQuestion::query()->findOrFail((int) $wrongQuestion);
        abort_if($wrongQuestion->user_id !== $this->currentUser($request)->id, 403);

        $wrongQuestion->update([
            'mastered_at' => now(),
            'consecutive_correct_count' => max(3, (int) $wrongQuestion->consecutive_correct_count),
        ]);

        return ApiResponse::success($wrongQuestion->fresh(), '已移出错题');
    }

    #[Apidoc\Title('收藏和笔记')]
    #[Apidoc\Url('/app/favorites')]
    #[Apidoc\Method('POST')]
    public function favorite(Request $request): JsonResponse
    {
        $data = $request->validate([
            'question_id' => ['required', 'exists:questions,id'],
            'note' => ['nullable', 'string'],
        ]);

        $favorite = FavoriteQuestion::updateOrCreate([
            'user_id' => $this->currentUser($request)->id,
            'question_id' => $data['question_id'],
        ], [
            'note' => $data['note'] ?? null,
        ]);

        return ApiResponse::success($favorite, '已保存');
    }

    private function currentUser(Request $request): User
    {
        return JWTAuth::parseToken()->authenticate() ?? auth('api')->user() ?? $request->user();
    }

    private function resolveAttempt(mixed $attempt): QuizAttempt
    {
        if ($attempt instanceof QuizAttempt && $attempt->exists) {
            return $attempt;
        }

        return QuizAttempt::query()->findOrFail((int) $attempt);
    }

    private function resolveBank(mixed $bank): QuestionBank
    {
        if ($bank instanceof QuestionBank && $bank->exists) {
            return $bank;
        }

        return QuestionBank::query()->findOrFail((int) $bank);
    }

    private function resolvePaper(mixed $paper): Paper
    {
        if ($paper instanceof Paper && $paper->exists) {
            return $paper;
        }

        return Paper::query()->findOrFail((int) $paper);
    }
}
