<?php

namespace App\Services;

use App\Models\PreQuestionCandidate;
use App\Models\Question;
use App\Models\QuestionKpRelation;
use App\Models\QuestionMeta;
use App\Models\KnowledgePoint;
use App\Models\TextbookCatalog;
use App\Services\AiKnowledgeService;
use App\Services\AiSolutionService;
use App\Services\QuestionGenerationService;
use App\Services\PdfStorageService;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;

class QuestionCandidateToQuestionService
{
    public function __construct(private readonly PdfStorageService $uploader)
    {
    }

    /**
     * 将源卷子下已校对的候选题入库到 questions。
     */
    public function promoteFromSourcePapers(Collection $papers): array
    {
        $summary = [
            'processed' => 0,
            'skipped' => 0,
            'errors' => 0,
        ];

        foreach ($papers as $paper) {
            $candidates = $paper->candidates()
                ->whereIn('status', [
                    PreQuestionCandidate::STATUS_REVIEWED,
                    PreQuestionCandidate::STATUS_PENDING,
                    PreQuestionCandidate::STATUS_ACCEPTED,
                ])
                ->where('is_question_candidate', true)
                ->get();

            $result = $this->promoteCandidates($candidates);
            $summary['processed'] += $result['processed'];
            $summary['skipped'] += $result['skipped'];
            $summary['errors'] += $result['errors'];
        }

        return $summary;
    }

    /**
     * 将候选题集合批量入库到 questions。
     */
    public function promoteCandidates(Collection $candidates): array
    {
        $summary = [
            'processed' => 0,
            'skipped' => 0,
            'errors' => 0,
        ];

        foreach ($candidates as $candidate) {
            try {
                $this->hydrateQuestionDetails($candidate);
                $this->hydrateKnowledgePoints($candidate);

                $validationErrors = $this->validateCandidate($candidate);
                if (!empty($validationErrors)) {
                    $summary['errors']++;
                    Log::warning('Candidate validation failed during promotion', [
                        'candidate_id' => $candidate->id,
                        'errors' => $validationErrors,
                    ]);
                    continue;
                }

                $question = $this->promoteCandidate($candidate);
                if ($question) {
                    $summary['processed']++;
                } else {
                    $summary['skipped']++;
                }
            } catch (\Throwable $e) {
                $summary['errors']++;
                Log::error('Failed to promote candidate to question', [
                    'candidate_id' => $candidate->id,
                    'error' => $e->getMessage(),
                ]);
            }
        }

        return $summary;
    }

    private function promoteCandidate(PreQuestionCandidate $candidate): ?Question
    {
        $meta = $candidate->meta ?? [];
        if (!empty($meta['question_id'])) {
            $existing = Question::query()->find($meta['question_id']);
            if ($existing) {
                return null;
            }
            Log::warning('Candidate has stale question_id, re-promoting', [
                'candidate_id' => $candidate->id,
                'question_id' => $meta['question_id'],
            ]);
            unset($meta['question_id']);
        }

        return DB::transaction(function () use ($candidate, $meta) {
            $generated = $meta['generated_question'] ?? [];
            $uploadedImages = $this->uploadCandidateImages($candidate);
            $questionType = $this->normalizeQuestionType(
                is_string($meta['question_type'] ?? null) ? $meta['question_type'] : ($generated['question_type'] ?? null),
                $candidate
            );

            $kpCodes = $this->normalizeKpCodes($meta['kp_codes'] ?? ($generated['knowledge_points'] ?? []));
            $primaryKp = $kpCodes[0] ?? null;
            $solutionSteps = $this->resolveSolutionSteps($candidate, $questionType, $meta);
            if (!empty($solutionSteps['steps'])) {
                $meta['solution_steps'] = $solutionSteps['steps'];
            }
            if (!empty($solutionSteps['solution']) && empty($meta['solution'])) {
                $meta['solution'] = $solutionSteps['solution'];
            }

            $questionCode = sprintf('CAND-%d', $candidate->id);
            $options = $candidate->options ?: ($generated['options'] ?? null);
            if (is_string($options)) {
                $decoded = json_decode($options, true);
                $options = is_array($decoded) ? $decoded : null;
            }

            $question = Question::updateOrCreate(
                ['question_code' => $questionCode],
                [
                    'question_type' => $questionType,
                    'kp_code' => $primaryKp,
                    'stem' => $candidate->stem ?: ($generated['stem'] ?? null) ?: $candidate->raw_text ?: $candidate->raw_markdown,
                    'options' => $options,
                    'answer' => $meta['answer'] ?? ($generated['answer'] ?? null),
                    'solution' => $meta['solution'] ?? ($generated['solution'] ?? null),
                    'difficulty' => $meta['difficulty'] ?? ($generated['difficulty'] ?? null),
                    'source_file_id' => $candidate->source_file_id,
                    'source_paper_id' => $candidate->source_paper_id,
                    'paper_part_id' => $candidate->part_id,
                    'textbook_id' => $candidate->sourcePaper?->textbook_id,
                    'source' => 'markdown_import',
                    'tags' => $this->joinTags($meta['tags'] ?? []),
                    'meta' => [
                        'candidate_id' => $candidate->id,
                        'import_id' => $candidate->import_id,
                        'images' => $uploadedImages,
                        'solution_steps' => $meta['solution_steps'] ?? [],
                        'generated_question' => $generated,
                    ],
                ]
            );

            $abilities = $meta['abilities'] ?? ($generated['abilities'] ?? []);
            QuestionMeta::updateOrCreate(
                ['question_id' => $question->id],
                [
                    'abilities' => $abilities,
                    'generation_info' => [
                        'source' => 'candidate',
                        'candidate_id' => $candidate->id,
                    ],
                    'review_status' => 'reviewed',
                ]
            );

            $this->syncKpRelations($question->id, $kpCodes);

            $meta['question_id'] = $question->id;
            $meta['images_uploaded'] = !empty($uploadedImages);
            $candidate->update([
                'images' => $uploadedImages,
                'status' => PreQuestionCandidate::STATUS_ACCEPTED,
                'meta' => $meta,
            ]);

            return $question;
        });
    }

    private function uploadCandidateImages(PreQuestionCandidate $candidate): array
    {
        $meta = $candidate->meta ?? [];
        $images = $candidate->images ?? [];
        if (is_string($images)) {
            $decoded = json_decode($images, true);
            $images = is_array($decoded) ? $decoded : [];
        }

        if (empty($images)) {
            return [];
        }

        if (!empty($meta['images_uploaded'])) {
            return $images;
        }

        $uploadedUrls = [];
        foreach ($images as $idx => $url) {
            $extension = pathinfo(parse_url((string) $url, PHP_URL_PATH) ?? '', PATHINFO_EXTENSION) ?: 'jpg';
            $path = "questions/images/{$candidate->id}_{$idx}.{$extension}";
            $uploadedUrls[] = $this->uploader->put($path, (string)@file_get_contents($url)) ?: $url;
        }

        return $uploadedUrls;
    }

    private function normalizeQuestionType(?string $type, PreQuestionCandidate $candidate): string
    {
        $type = strtolower(trim((string) $type));
        $map = [
            'choice' => 'choice',
            'fill' => 'fill',
            'answer' => 'answer',
            '选择题' => 'choice',
            '填空题' => 'fill',
            '解答题' => 'answer',
            '简答题' => 'answer',
            '计算题' => 'answer',
        ];

        if ($type !== '' && isset($map[$type])) {
            return $map[$type];
        }

        if (!empty($candidate->options)) {
            return 'choice';
        }

        $stem = (string) ($candidate->stem ?? $candidate->raw_markdown ?? '');
        if (preg_match('/_{2,}|\\(\\s*\\)/u', $stem)) {
            return 'fill';
        }

        return 'answer';
    }

    private function normalizeKpCodes(array|string $kpCodes): array
    {
        if (is_string($kpCodes)) {
            $kpCodes = preg_split('/[，,\\s]+/u', $kpCodes) ?: [];
        }

        return array_values(array_filter(array_map('trim', $kpCodes)));
    }

    private function syncKpRelations(int $questionId, array $kpCodes): void
    {
        if (empty($kpCodes)) {
            return;
        }

        QuestionKpRelation::query()
            ->where('question_id', $questionId)
            ->whereNotIn('kp_code', $kpCodes)
            ->delete();

        foreach ($kpCodes as $code) {
            QuestionKpRelation::updateOrCreate(
                [
                    'question_id' => $questionId,
                    'kp_code' => $code,
                ],
                [
                    'weight' => 1.0,
                ]
            );
        }
    }

    private function joinTags(array|string $tags): ?string
    {
        if (is_string($tags)) {
            $tags = preg_split('/[，,\\s]+/u', $tags) ?: [];
        }

        $tags = array_values(array_filter(array_map('trim', $tags)));
        return empty($tags) ? null : implode(',', $tags);
    }

    /**
     * 验证候选题是否具备入库条件
     */
    public function validateCandidate(PreQuestionCandidate $candidate): array
    {
        $errors = [];
        $meta = $candidate->meta ?? [];

        if (empty($candidate->stem) && empty($candidate->raw_markdown)) {
            $errors[] = '题干内容为空';
        }

        if (empty($candidate->sourcePaper?->textbook_id)) {
            $errors[] = '未设置教材信息';
        }

        if (empty($meta['question_type']) && empty($candidate->question_type)) {
            // 虽然 promoteCandidate 会自动兜底题型，但建议在校对阶段明确
        }

        return $errors;
    }

    private function hydrateQuestionDetails(PreQuestionCandidate $candidate): void
    {
        $meta = $candidate->meta ?? [];
        $generated = $meta['generated_question'] ?? [];
        if (!empty($generated)) {
            return;
        }

        $needs = false;
        $fields = [
            $meta['answer'] ?? null,
            $meta['solution'] ?? null,
            $meta['difficulty'] ?? null,
            $meta['question_type'] ?? null,
        ];
        foreach ($fields as $value) {
            if (empty($value)) {
                $needs = true;
                break;
            }
        }

        if (empty($meta['kp_codes']) && empty($candidate->kp_code)) {
            $needs = true;
        }

        $questionType = $this->normalizeQuestionType(
            is_string($meta['question_type'] ?? null) ? $meta['question_type'] : null,
            $candidate
        );
        if ($questionType === 'answer' && empty($meta['solution_steps'])) {
            $needs = true;
        }

        if (!$needs) {
            return;
        }

        $questionText = (string) ($candidate->stem ?: $candidate->raw_text ?: $candidate->raw_markdown);
        if ($questionText === '') {
            return;
        }

        $context = $this->buildGenerationContext($candidate->sourcePaper);
        $images = $candidate->images ?? [];
        if (is_string($images)) {
            $decoded = json_decode($images, true);
            $images = is_array($decoded) ? $decoded : [];
        }
        $imageText = '';
        if (!empty($images)) {
            $imageText = "image_urls:\n" . implode("\n", array_map('strval', $images));
        }

        $parts = [];
        if ($context) {
            $parts[] = $context;
        }
        if ($imageText !== '') {
            $parts[] = $imageText;
        }
        $parts[] = "题目内容：\n" . $questionText;
        $sourceText = implode("\n\n", $parts);

        $result = app(QuestionGenerationService::class)->generateFromSource($sourceText);
        if (empty($result['success'])) {
            Log::warning('AI question generation failed', [
                'candidate_id' => $candidate->id,
                'message' => $result['message'] ?? 'unknown',
            ]);
            return;
        }

        $generated = $result['question'] ?? [];
        if (empty($generated)) {
            return;
        }

        if (empty($meta['answer']) && !empty($generated['answer'])) {
            $meta['answer'] = $generated['answer'];
        }
        if (empty($meta['solution']) && !empty($generated['solution'])) {
            $meta['solution'] = $generated['solution'];
        }
        if (empty($meta['difficulty']) && isset($generated['difficulty'])) {
            $meta['difficulty'] = $generated['difficulty'];
        }
        if (empty($meta['question_type']) && !empty($generated['question_type'])) {
            $meta['question_type'] = $generated['question_type'];
        }
        if (empty($meta['kp_codes']) && !empty($generated['knowledge_points'])) {
            $meta['kp_codes'] = $generated['knowledge_points'];
        }
        if (empty($meta['solution_steps']) && !empty($generated['solution_steps'])) {
            $meta['solution_steps'] = $generated['solution_steps'];
        }
        if (empty($meta['abilities']) && !empty($generated['abilities'])) {
            $meta['abilities'] = $generated['abilities'];
        }

        $meta['generated_question'] = $generated;
        $candidate->meta = $meta;
    }

    private function hydrateKnowledgePoints(PreQuestionCandidate $candidate): void
    {
        $meta = $candidate->meta ?? [];
        $kpCodes = $this->normalizeKpCodes($meta['kp_codes'] ?? ($candidate->kp_code ?? []));
        if (!empty($kpCodes)) {
            return;
        }

        $questionText = (string) ($candidate->stem ?: $candidate->raw_text ?: $candidate->raw_markdown);
        if ($questionText === '') {
            return;
        }

        $paper = $candidate->sourcePaper;
        $context = $this->buildKnowledgeMatchContext($paper);
        $candidates = $this->buildKnowledgePointPool($paper);

        $matches = app(AiKnowledgeService::class)->matchKnowledgePointsByAi($questionText, $candidates, $context);
        if (empty($matches)) {
            Log::warning('AI knowledge match returned empty', [
                'candidate_id' => $candidate->id,
                'paper_id' => $candidate->source_paper_id,
            ]);
            return;
        }

        $meta['kp_codes'] = array_values(array_unique(array_filter(array_map(
            fn ($item) => $item['kp_code'] ?? null,
            $matches
        ))));
        $meta['kp_weights'] = $matches;

        $candidate->meta = $meta;
    }

    private function buildKnowledgeMatchContext(?\App\Models\SourcePaper $paper): ?string
    {
        if (!$paper) {
            return null;
        }

        $lines = [];
        if (!empty($paper->grade)) {
            $lines[] = '年级: ' . $paper->grade;
        }
        if (!empty($paper->term)) {
            $lines[] = '学期: ' . $paper->term;
        }
        if (!empty($paper->textbook_id)) {
            $textbook = $paper->textbook;
            if ($textbook) {
                $lines[] = '教材: ' . ($textbook->official_title ?? $textbook->title ?? $textbook->id);
            }
        }

        $meta = $paper->meta ?? [];
        $catalogIds = $meta['catalog_node_ids'] ?? ($meta['catalog_node_id'] ?? []);
        $catalogIds = $this->normalizeCatalogNodeIds($catalogIds);
        if (!empty($catalogIds)) {
            $titles = TextbookCatalog::query()
                ->whereIn('id', $catalogIds)
                ->pluck('title')
                ->filter()
                ->take(6)
                ->toArray();
            if (!empty($titles)) {
                $lines[] = '目录: ' . implode(' / ', $titles);
            }
        }

        return empty($lines) ? null : implode("\n", $lines);
    }

    private function buildGenerationContext(?\App\Models\SourcePaper $paper): ?string
    {
        return $this->buildKnowledgeMatchContext($paper);
    }

    private function buildKnowledgePointPool(?\App\Models\SourcePaper $paper): array
    {
        $query = KnowledgePoint::query()->select(['kp_code', 'name']);
        if ($paper?->grade) {
            $query->where('grade', $paper->grade);
        }
        if ($paper?->subject) {
            $query->where('subject', $paper->subject);
        }

        return $query->orderBy('kp_code')->limit(300)->get()->toArray();
    }

    private function resolveSolutionSteps(PreQuestionCandidate $candidate, string $questionType, array $meta): array
    {
        if ($questionType !== 'answer') {
            return [
                'solution' => $meta['solution'] ?? '',
                'steps' => [],
            ];
        }

        if (!empty($meta['solution_steps'])) {
            return [
                'solution' => $meta['solution'] ?? '',
                'steps' => $meta['solution_steps'],
            ];
        }

        $questionText = (string) ($candidate->stem ?: $candidate->raw_text ?: $candidate->raw_markdown);
        if ($questionText === '') {
            return [
                'solution' => $meta['solution'] ?? '',
                'steps' => [],
            ];
        }

        $result = app(AiSolutionService::class)->generateSolutionSteps($questionText);
        return [
            'solution' => $result['solution'] ?? '',
            'steps' => $result['steps'] ?? [],
        ];
    }

    private function normalizeCatalogNodeIds(array|string $value): array
    {
        $ids = is_array($value) ? $value : [$value];
        $ids = array_values(array_unique(array_map('intval', array_filter($ids))));
        return $ids;
    }
}
