<?php

namespace App\Filament\Pages;

use App\Models\KnowledgePoint;
use App\Models\PaperPart;
use App\Models\PreQuestionCandidate;
use App\Models\SourcePaper;
use App\Services\AiKnowledgeService;
use App\Services\AiSolutionService;
use App\Services\QuestionGenerationService;
use Filament\Pages\Page;
use Illuminate\Support\Arr;

class QuestionCandidateWorkbench extends Page
{
    protected static ?string $navigationLabel = '题目人工补录';
    protected static string|\BackedEnum|null $navigationIcon = 'heroicon-o-pencil-square';
    protected static string|\UnitEnum|null $navigationGroup = '卷子导入流程';
    protected static ?int $navigationSort = 4;

    protected string $view = 'filament.pages.question-candidate-workbench';

    public string $search = '';
    public ?string $statusFilter = null;
    public ?int $sourcePaperFilter = null;
    public ?int $partFilter = null;
    public string $viewMode = 'list';
    public bool $dense = false;
    public string $kpSearch = '';
    public string $aiBatchMode = 'missing';

    public array $selectedIds = [];
    public ?int $currentId = null;

    public array $form = [
        'question_type' => null,
        'difficulty' => null,
        'score' => null,
        'kp_codes' => [],
        'tags' => '',
        'stem' => null,
        'options' => '',
        'images' => [],
        'source_paper_id' => null,
        'part_id' => null,
        'order_index' => null,
        'answer' => null,
        'solution' => null,
        'solution_steps' => '',
    ];

    public array $batch = [
        'question_type' => null,
        'difficulty' => null,
        'score' => null,
        'kp_codes' => [],
        'tags' => '',
        'part_id' => null,
        'source_paper_id' => null,
    ];

    public function mount(): void
    {
        $first = $this->candidates()->first();
        if ($first) {
            $this->selectCandidate($first->id);
        }
    }

    public function candidates()
    {
        $query = PreQuestionCandidate::query()->with(['sourcePaper', 'part']);

        if ($this->search !== '') {
            $query->where(function ($q) {
                $q->where('stem', 'like', '%' . $this->search . '%')
                    ->orWhere('raw_markdown', 'like', '%' . $this->search . '%');
            });
        }

        if ($this->statusFilter) {
            $query->where('status', $this->statusFilter);
        }

        if ($this->viewMode === 'review') {
            $query->where('status', 'pending');
        }

        if ($this->sourcePaperFilter) {
            $query->where('source_paper_id', $this->sourcePaperFilter);
        }

        if ($this->partFilter) {
            $query->where('part_id', $this->partFilter);
        }

        return $query->orderBy('sequence')->limit(120)->get();
    }

    public function selectCandidate(int $candidateId): void
    {
        $candidate = PreQuestionCandidate::query()->find($candidateId);
        if (!$candidate) {
            return;
        }

        $this->currentId = $candidateId;
        $meta = $candidate->meta ?? [];

        $this->form = [
            'question_type' => Arr::get($meta, 'question_type'),
            'difficulty' => Arr::get($meta, 'difficulty'),
            'score' => Arr::get($meta, 'score'),
            'kp_codes' => Arr::get($meta, 'kp_codes', []),
            'tags' => implode(',', Arr::get($meta, 'tags', [])),
            'stem' => $candidate->stem,
            'options' => $candidate->options ? json_encode($candidate->options, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) : '',
            'images' => is_array($candidate->images) ? implode(',', $candidate->images) : (string) ($candidate->images ?? ''),
            'source_paper_id' => $candidate->source_paper_id,
            'part_id' => $candidate->part_id,
            'order_index' => $candidate->order_index,
            'answer' => Arr::get($meta, 'answer'),
            'solution' => Arr::get($meta, 'solution'),
            'solution_steps' => json_encode(Arr::get($meta, 'solution_steps', []), JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT),
        ];
    }

    public function saveCandidate(): void
    {
        $candidate = $this->currentCandidate();
        if (!$candidate) {
            return;
        }

        $options = null;
        if ($this->form['options']) {
            $decoded = json_decode((string) $this->form['options'], true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $options = $decoded;
            }
        }

        $steps = [];
        if ($this->form['solution_steps']) {
            $decoded = json_decode((string) $this->form['solution_steps'], true);
            if (json_last_error() === JSON_ERROR_NONE) {
                $steps = $decoded;
            }
        }

        $meta = $candidate->meta ?? [];
        $meta['question_type'] = $this->form['question_type'] ?? null;
        $meta['difficulty'] = $this->form['difficulty'] ?? null;
        $meta['score'] = $this->form['score'] ?? null;
        $meta['kp_codes'] = $this->form['kp_codes'] ?? [];
        $meta['tags'] = $this->explodeTags($this->form['tags'] ?? '');
        $meta['answer'] = $this->form['answer'] ?? null;
        $meta['solution'] = $this->form['solution'] ?? null;
        $meta['solution_steps'] = $steps;

        $candidate->update([
            'stem' => $this->form['stem'],
            'options' => $options,
            'images' => $this->explodeTags((string) ($this->form['images'] ?? '')),
            'source_paper_id' => $this->form['source_paper_id'],
            'part_id' => $this->form['part_id'],
            'order_index' => $this->form['order_index'],
            'meta' => $meta,
            'status' => 'reviewed',
        ]);
    }

    public function applyBatch(): void
    {
        if (empty($this->selectedIds)) {
            return;
        }

        foreach (PreQuestionCandidate::query()->whereIn('id', $this->selectedIds)->get() as $candidate) {
            $meta = $candidate->meta ?? [];

            if (!empty($this->batch['question_type'])) {
                $meta['question_type'] = $this->batch['question_type'];
            }
            if (!empty($this->batch['difficulty'])) {
                $meta['difficulty'] = $this->batch['difficulty'];
            }
            if (!empty($this->batch['score'])) {
                $meta['score'] = $this->batch['score'];
            }
            if (!empty($this->batch['kp_codes'])) {
                $meta['kp_codes'] = $this->batch['kp_codes'];
            }
            if (!empty($this->batch['tags'])) {
                $meta['tags'] = $this->explodeTags($this->batch['tags']);
            }

            $candidate->update([
                'part_id' => $this->batch['part_id'] ?: $candidate->part_id,
                'source_paper_id' => $this->batch['source_paper_id'] ?: $candidate->source_paper_id,
                'meta' => $meta,
                'status' => 'reviewed',
            ]);
        }
    }

    public function seedBatchFromCurrent(): void
    {
        $candidate = $this->currentCandidate();
        if (!$candidate) {
            return;
        }

        $meta = $candidate->meta ?? [];
        $this->batch = [
            'question_type' => Arr::get($meta, 'question_type'),
            'difficulty' => Arr::get($meta, 'difficulty'),
            'score' => Arr::get($meta, 'score'),
            'kp_codes' => Arr::get($meta, 'kp_codes', []),
            'tags' => implode(',', Arr::get($meta, 'tags', [])),
            'part_id' => $candidate->part_id,
            'source_paper_id' => $candidate->source_paper_id,
        ];
    }

    public function aiAutoFill(): void
    {
        $candidate = $this->currentCandidate();
        if (!$candidate) {
            return;
        }

        $sourceText = $candidate->stem ?: (string) $candidate->raw_markdown;
        $result = app(QuestionGenerationService::class)->generateFromSource($sourceText);
        if (!($result['success'] ?? false)) {
            return;
        }

        $question = $result['question'] ?? [];
        $meta = $candidate->meta ?? [];
        $meta['question_type'] = $question['question_type'] ?? $meta['question_type'] ?? null;
        $meta['difficulty'] = $question['difficulty'] ?? $meta['difficulty'] ?? null;
        $meta['kp_codes'] = $question['knowledge_points'] ?? $meta['kp_codes'] ?? [];
        $meta['answer'] = $question['answer'] ?? $meta['answer'] ?? null;
        $meta['solution'] = $question['solution'] ?? $meta['solution'] ?? null;
        $meta['solution_steps'] = $question['solution_steps'] ?? $meta['solution_steps'] ?? [];

        $candidate->update([
            'stem' => $question['stem'] ?? $candidate->stem,
            'options' => $question['options'] ?? $candidate->options,
            'meta' => $meta,
        ]);

        $this->selectCandidate($candidate->id);
    }

    public function aiBatchAutoFill(): void
    {
        if (empty($this->selectedIds)) {
            return;
        }

        $mode = $this->aiBatchMode;
        $candidates = PreQuestionCandidate::query()->whereIn('id', $this->selectedIds)->get();

        foreach ($candidates as $candidate) {
            $sourceText = $candidate->stem ?: (string) $candidate->raw_markdown;
            $result = app(QuestionGenerationService::class)->generateFromSource($sourceText);
            if (!($result['success'] ?? false)) {
                continue;
            }

            $question = $result['question'] ?? [];
            $meta = $candidate->meta ?? [];

            $meta['question_type'] = $this->fillValue($meta['question_type'] ?? null, $question['question_type'] ?? null, $mode);
            $meta['difficulty'] = $this->fillValue($meta['difficulty'] ?? null, $question['difficulty'] ?? null, $mode);
            $meta['kp_codes'] = $this->fillArray($meta['kp_codes'] ?? [], $question['knowledge_points'] ?? [], $mode);
            $meta['answer'] = $this->fillValue($meta['answer'] ?? null, $question['answer'] ?? null, $mode);
            $meta['solution'] = $this->fillValue($meta['solution'] ?? null, $question['solution'] ?? null, $mode);
            $meta['solution_steps'] = $this->fillArray($meta['solution_steps'] ?? [], $question['solution_steps'] ?? [], $mode);

            $updates = ['meta' => $meta];
            if ($mode === 'overwrite' || empty($candidate->stem)) {
                if (!empty($question['stem'])) {
                    $updates['stem'] = $question['stem'];
                }
            }
            if ($mode === 'overwrite' || empty($candidate->options)) {
                if (!empty($question['options'])) {
                    $updates['options'] = $question['options'];
                }
            }

            $candidate->update($updates);
        }
    }

    public function applyDifficultyByOrder(): void
    {
        if (empty($this->selectedIds)) {
            return;
        }

        $candidates = PreQuestionCandidate::query()
            ->whereIn('id', $this->selectedIds)
            ->get()
            ->groupBy(fn ($candidate) => $candidate->part_id ?: $candidate->source_paper_id);

        foreach ($candidates as $group) {
            $sorted = $group->sortBy(function ($candidate) {
                return $candidate->order_index ?? $candidate->sequence ?? $candidate->index ?? $candidate->id;
            })->values();

            $total = max(1, $sorted->count());
            foreach ($sorted as $index => $candidate) {
                $difficulty = (int) ceil((($index + 1) / $total) * 5);
                $difficulty = max(1, min(5, $difficulty));

                $meta = $candidate->meta ?? [];
                $meta['difficulty'] = $difficulty;
                $candidate->update(['meta' => $meta]);
            }
        }
    }

    public function aiMatchKnowledge(): void
    {
        $candidate = $this->currentCandidate();
        if (!$candidate) {
            return;
        }

        $text = $candidate->stem ?: (string) $candidate->raw_markdown;
        $matches = app(AiKnowledgeService::class)->matchKnowledgePointsByAi($text);
        $kpCodes = array_values(array_filter(array_map(fn ($item) => $item['kp_code'] ?? null, $matches)));

        $meta = $candidate->meta ?? [];
        $meta['kp_codes'] = $kpCodes;
        $candidate->update(['meta' => $meta]);

        $this->selectCandidate($candidate->id);
    }

    public function aiGenerateSolution(): void
    {
        $candidate = $this->currentCandidate();
        if (!$candidate) {
            return;
        }

        $result = app(AiSolutionService::class)->generateSolution($candidate->stem ?? '');
        $meta = $candidate->meta ?? [];
        $meta['solution'] = $result['solution'] ?? '';
        $meta['solution_steps'] = $result['steps'] ?? [];
        $candidate->update(['meta' => $meta]);

        $this->selectCandidate($candidate->id);
    }

    public function nextCandidate(): void
    {
        $ids = $this->candidates()->pluck('id')->values();
        $index = $ids->search($this->currentId);
        if ($index !== false && isset($ids[$index + 1])) {
            $this->selectCandidate($ids[$index + 1]);
        }
    }

    public function previousCandidate(): void
    {
        $ids = $this->candidates()->pluck('id')->values();
        $index = $ids->search($this->currentId);
        if ($index !== false && $index > 0) {
            $this->selectCandidate($ids[$index - 1]);
        }
    }

    public function knowledgePointOptions(): array
    {
        return KnowledgePoint::query()->orderBy('kp_code')->pluck('name', 'kp_code')->toArray();
    }

    public function knowledgePointTreeOptions(): array
    {
        $points = KnowledgePoint::query()
            ->orderBy('kp_code')
            ->get(['kp_code', 'name', 'parent_kp_code'])
            ->groupBy('parent_kp_code');

        $walk = function ($parent, int $depth) use (&$walk, $points): array {
            $items = [];
            foreach ($points->get($parent, collect()) as $point) {
                $indent = str_repeat('—', $depth);
                $label = trim($indent . ' ' . $point->name);
                $items[$point->kp_code] = $label;
                $items += $walk($point->kp_code, $depth + 1);
            }
            return $items;
        };

        return $walk(null, 0);
    }

    public function filteredKnowledgePointOptions(): array
    {
        $options = $this->knowledgePointTreeOptions();
        if ($this->kpSearch === '') {
            return $options;
        }

        $needle = mb_strtolower($this->kpSearch);
        return array_filter($options, fn ($label, $code) => str_contains(mb_strtolower($label), $needle) || str_contains(mb_strtolower($code), $needle), ARRAY_FILTER_USE_BOTH);
    }

    public function sourcePaperOptions(): array
    {
        return SourcePaper::query()->orderByDesc('id')->limit(200)->pluck('title', 'id')->toArray();
    }

    public function partOptions(): array
    {
        return PaperPart::query()->orderByDesc('id')->limit(200)->pluck('title', 'id')->toArray();
    }

    public function currentCandidate(): ?PreQuestionCandidate
    {
        return $this->currentId ? PreQuestionCandidate::query()->find($this->currentId) : null;
    }

    private function explodeTags(string $tags): array
    {
        return array_values(array_filter(array_map('trim', explode(',', $tags))));
    }

    private function fillValue($current, $incoming, string $mode)
    {
        if ($mode === 'overwrite') {
            return $incoming ?? $current;
        }

        return $current !== null && $current !== '' ? $current : $incoming;
    }

    private function fillArray(array $current, array $incoming, string $mode): array
    {
        if ($mode === 'overwrite') {
            return !empty($incoming) ? $incoming : $current;
        }

        return !empty($current) ? $current : $incoming;
    }
}
