<?php

namespace App\Services;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Collection;

/**
 * 知识掌握度计算引擎（PHP版本）
 * 基于学案基准难度的动态加减逻辑计算掌握度
 *
 * 新算法特点：
 * 1. 根据学案基准难度（筑基、提分、培优、竞赛）动态调整权重
 * 2. 难度映射：0.0-1.0 → 1-4级
 * 3. 权重计算：越级、适应、降级三种情况
 * 4. 父节点掌握度：子节点平均值
 */
class MasteryCalculator
{
    /**
     * 掌握度阈值配置
     */
    private const MASTERY_THRESHOLD_WEAK = 0.50;   // 薄弱点阈值
    private const MASTERY_THRESHOLD_GOOD = 0.70;   // 良好阈值
    private const MASTERY_THRESHOLD_MASTER = 0.85; // 掌握阈值

    /**
     * 最小正确率要求
     */
    private const MIN_CORRECT_RATE = 0.60;

    /**
     * 计算学生对指定知识点的掌握度
     *
     * @param string $studentId 学生ID
     * @param string $kpCode 知识点编码
     * @param array|null $attempts 答题记录（可选，默认从数据库查询）
     * @param int|null $examBaseDifficulty 学案基准难度（1-4级，1=筑基, 2=提分, 3=培优, 4=竞赛）
     * @return array 返回['mastery' => 掌握度, 'confidence' => 置信度, 'trend' => 趋势]
     */
    public function calculateMasteryLevel(string $studentId, string $kpCode, ?array $attempts = null, ?int $examBaseDifficulty = null): array
    {
        // 优先使用传递的答题记录，如果没有则从数据库查询
        if ($attempts === null || empty($attempts)) {
            $attempts = $this->getStudentAttempts($studentId, $kpCode);
        }

        if (empty($attempts)) {
            Log::warning('没有答题记录，无法计算掌握度', [
                'student_id' => $studentId,
                'kp_code' => $kpCode,
                'attempts_provided' => $attempts !== null,
            ]);
            // 【公司要求】返回值：只返回核心掌握度数据
            return [
                'mastery' => 0.0,
                'total_attempts' => 0,
                'correct_attempts' => 0,
                'accuracy_rate' => 0.0,
            ];
        }

        // 如果没有学案基准难度，使用默认值2（提分）
        if ($examBaseDifficulty === null) {
            Log::warning('缺少学案基准难度，使用默认值2（提分）', [
                'student_id' => $studentId,
                'kp_code' => $kpCode,
                'attempts_count' => count($attempts),
            ]);
            $examBaseDifficulty = 2; // 默认提分难度
        }

        $masteryData = $this->calculateMasteryWithExamDifficulty($studentId, $kpCode, $attempts, $examBaseDifficulty);

        // 【公司要求】日志输出：只记录核心掌握度数据
        Log::info('掌握度计算完成', [
            'student_id' => $studentId,
            'kp_code' => $kpCode,
            'exam_base_difficulty' => $examBaseDifficulty,
            'difficulty_name' => $this->getDifficultyName($examBaseDifficulty),
            'total_attempts' => count($attempts),
            'correct_attempts' => $masteryData['correct_attempts'],
            'final_mastery' => $masteryData['mastery'],
        ]);

        return $masteryData;
    }

    /**
     * 获取难度等级名称
     */
    private function getDifficultyName(int $difficultyLevel): string
    {
        return match ($difficultyLevel) {
            1 => '筑基',
            2 => '提分',
            3 => '培优',
            4 => '竞赛',
            default => '未知',
        };
    }

    /**
     * 【新算法】使用学案基准难度的动态加减逻辑计算掌握度
     */
    private function calculateMasteryWithExamDifficulty(string $studentId, string $kpCode, array $attempts, int $examBaseDifficulty): array
    {
        // 获取历史掌握度
        $historyMastery = DB::table('student_knowledge_mastery')
            ->where('student_id', $studentId)
            ->where('kp_code', $kpCode)
            ->first();

        $oldMastery = $historyMastery->mastery_level ?? 0.0; // 默认 0.0

        // 统计正确和错误次数
        $totalAttempts = count($attempts);
        $correctAttempts = 0;
        $incorrectAttempts = 0;

        // 计算每次答题的权重变化
        $totalChange = 0.0;

        foreach ($attempts as $attempt) {
            $isCorrect = boolval($attempt['is_correct'] ?? false);
            $questionDifficulty = floatval($attempt['question_difficulty'] ?? 0.6);

            // 难度映射：将0.0-1.0的浮点数难度映射为 1-4 等级
            $questionLevel = $this->mapDifficultyToLevel($questionDifficulty);

            // 根据难度关系计算权重变化
            $change = $this->calculateWeightByDifficultyRelation($questionLevel, $examBaseDifficulty, $isCorrect);

            $totalChange += $change;

            if ($isCorrect) {
                $correctAttempts++;
            } else {
                $incorrectAttempts++;
            }

            Log::debug('掌握度变化计算', [
                'question_id' => $attempt['question_id'] ?? '',
                'question_difficulty' => $questionDifficulty,
                'question_level' => $questionLevel,
                'exam_base_difficulty' => $examBaseDifficulty,
                'is_correct' => $isCorrect,
                'change' => $change,
                'running_total' => $totalChange
            ]);
        }

        // 【公司要求】数值更新：newMastery = oldMastery + change
        $newMastery = $oldMastery + $totalChange;

        // 【公司要求】边界限制：0.0 ~ 1.0
        $newMastery = max(0.0, min(1.0, $newMastery));

        // 【公司要求】保存到数据库（只保存核心掌握度数据）
        DB::table('student_knowledge_mastery')
            ->updateOrInsert(
                ['student_id' => $studentId, 'kp_code' => $kpCode],
                [
                    'mastery_level' => $newMastery,
                    'confidence_level' => 0.0, // 不再计算置信度
                    'total_attempts' => ($historyMastery->total_attempts ?? 0) + $totalAttempts,
                    'correct_attempts' => ($historyMastery->correct_attempts ?? 0) + $correctAttempts,
                    'mastery_trend' => 'stable', // 不再判断趋势，统一设为stable
                    'last_mastery_update' => now(),
                    'updated_at' => now(),
                ]
            );

        // 【公司要求】返回值：只返回核心掌握度数据
        return [
            'mastery' => round($newMastery, 4),
            'total_attempts' => $totalAttempts,
            'correct_attempts' => $correctAttempts,
            'accuracy_rate' => round(($correctAttempts / $totalAttempts) * 100, 2),
            'old_mastery' => $oldMastery,
            'change' => round($totalChange, 4),
            'details' => [
                'exam_base_difficulty' => $examBaseDifficulty,
                'total_change' => round($totalChange, 4)
            ],
        ];
    }

    /**
     * 【公司要求】难度映射：将题目中0.0-1.0的浮点数难度映射为1-4等级
     *
     * 公司要求：
     * 0.0 ~ 0.25 -> 1级（筑基）
     * 0.25 ~ 0.5 -> 2级（提分）
     * 0.5 ~ 0.75 -> 3级（培优）
     * 0.75 ~ 1.0 -> 4级（竞赛）
     *
     * @param float $difficulty 题目难度（0.0-1.0浮点数）
     * @return int 难度等级（1-4级）
     */
    private function mapDifficultyToLevel(float $difficulty): int
    {
        if ($difficulty >= 0.0 && $difficulty < 0.25) {
            return 1; // 1级（筑基）
        } elseif ($difficulty >= 0.25 && $difficulty < 0.5) {
            return 2; // 2级（提分）
        } elseif ($difficulty >= 0.5 && $difficulty < 0.75) {
            return 3; // 3级（培优）
        } else {
            return 4; // 4级（竞赛）
        }
    }

    /**
     * 【公司要求】根据难度关系计算权重变化
     *
     * 公司要求的三种难度关系及权重：
     * 1. 越级（Question > {学案基准难度}）：对 +0.15 / 错 -0.05
     * 2. 适应（Question = {学案基准难度}）：对 +0.10 / 错 -0.10
     * 3. 降级（Question < {学案基准难度}）：对 +0.05 / 错 -0.15
     *
     * 学案基准难度获取：
     * - 来源：试卷表（papers.difficulty_category）
     * - 映射：筑基→1级，提分→2级，培优→3级，竞赛→4级
     *
     * @param int $questionLevel 题目难度等级（1-4）
     * @param int $examBaseDifficulty 学案基准难度（1-4）
     * @param bool $isCorrect 答题是否正确
     * @return float 权重变化值
     */
    private function calculateWeightByDifficultyRelation(int $questionLevel, int $examBaseDifficulty, bool $isCorrect): float
    {
        if ($questionLevel > $examBaseDifficulty) {
            // 越级：题目难度 > 学案基准难度
            return $isCorrect ? 0.15 : -0.05;
        } elseif ($questionLevel == $examBaseDifficulty) {
            // 适应：题目难度 = 学案基准难度
            return $isCorrect ? 0.10 : -0.10;
        } else {
            // 降级：题目难度 < 学案基准难度
            return $isCorrect ? 0.05 : -0.15;
        }
    }

    /**
     * 获取学生的答题记录
     */
    private function getStudentAttempts(string $studentId, string $kpCode): array
    {
        $attempts = [];

        // 优先从 student_answer_steps 表获取（步骤级记录）
        $stepAttempts = DB::table('student_answer_steps')
            ->where('student_id', $studentId)
            ->where('kp_id', $kpCode)
            ->orderBy('created_at', 'asc')
            ->get();

        foreach ($stepAttempts as $step) {
            // 【关键修复】从题目表查询题目难度
            $questionDifficulty = 0.6; // 默认难度
            if ($step->question_id) {
                $question = DB::table('questions')
                    ->where('id', $step->question_id)
                    ->orWhere('question_code', $step->question_id)
                    ->first();
                if ($question && $question->difficulty !== null) {
                    $questionDifficulty = floatval($question->difficulty);
                }
            }

            $attempts[] = [
                'student_id' => $step->student_id,
                'paper_id' => $step->exam_id,
                'question_id' => $step->question_id,
                'kp_code' => $step->kp_id,
                'is_correct' => (bool) $step->is_correct,
                'score_obtained' => $step->step_score,
                'max_score' => $step->step_score, // 步骤分数本身就是满分
                'question_difficulty' => $questionDifficulty, // 【新增】题目难度
                'created_at' => $step->created_at,
            ];
        }

        // 如果没有步骤级记录，从 student_answer_questions 表获取（题目级记录）
        if (empty($attempts)) {
            $questionAttempts = DB::table('student_answer_questions')
                ->where('student_id', $studentId)
                ->orderBy('created_at', 'asc')
                ->get();

            foreach ($questionAttempts as $question) {
                // 【关键修复】从题目表查询题目难度
                $questionDifficulty = 0.6; // 默认难度
                if ($question->question_id) {
                    $questionInfo = DB::table('questions')
                        ->where('id', $question->question_id)
                        ->orWhere('question_code', $question->question_id)
                        ->first();
                    if ($questionInfo && $questionInfo->difficulty !== null) {
                        $questionDifficulty = floatval($questionInfo->difficulty);
                    }
                }

                $attempts[] = [
                    'student_id' => $question->student_id,
                    'paper_id' => $question->exam_id,
                    'question_id' => $question->question_id,
                    'kp_code' => $kpCode, // 使用传入的kpCode
                    'is_correct' => ($question->score_obtained ?? 0) > 0,
                    'score_obtained' => $question->score_obtained ?? 0,
                    'max_score' => $question->max_score ?? 0,
                    'question_difficulty' => $questionDifficulty, // 【新增】题目难度
                    'created_at' => $question->created_at,
                ];
            }
        }

        return $attempts;
    }

    /**
     * 批量更新学生掌握度
     */
    public function batchUpdateMastery(string $studentId, array $kpCodes): array
    {
        $results = [];

        foreach ($kpCodes as $kpCode) {
            $masteryData = $this->calculateMasteryLevel($studentId, $kpCode);

            // 保存到数据库
            DB::table('student_knowledge_mastery')
                ->updateOrInsert(
                    ['student_id' => $studentId, 'kp_code' => $kpCode],
                    [
                        'mastery_level' => $masteryData['mastery'],
                        'confidence_level' => $masteryData['confidence'],
                        'total_attempts' => $masteryData['total_attempts'],
                        'correct_attempts' => $masteryData['correct_attempts'],
                        'mastery_trend' => $masteryData['trend'],
                        'last_mastery_update' => now(),
                        'updated_at' => now(),
                    ]
                );

            $results[$kpCode] = $masteryData;
        }

        return $results;
    }

    /**
     * 获取学生所有知识点的掌握度概览
     */
    public function getStudentMasteryOverview(string $studentId): array
    {
        $masteryList = DB::table('student_knowledge_mastery')
            ->where('student_id', $studentId)
            ->get();

        if ($masteryList->isEmpty()) {
            return [
                'total_knowledge_points' => 0,
                'average_mastery_level' => 0.0,
                'mastered_knowledge_points' => 0,
                'good_knowledge_points' => 0,
                'weak_knowledge_points' => 0,
                'weak_knowledge_points_list' => [],
                'details' => [],
            ];
        }

        $masteryArray = $masteryList->toArray();

        $total = count($masteryArray);
        $average = $masteryArray ? array_sum(array_column($masteryArray, 'mastery_level')) / $total : 0;

        $mastered = [];
        $good = [];
        $weak = [];

        foreach ($masteryArray as $item) {
            $level = floatval($item->mastery_level);
            if ($level >= self::MASTERY_THRESHOLD_MASTER) {
                $mastered[] = $item;
            } elseif ($level >= self::MASTERY_THRESHOLD_GOOD) {
                $good[] = $item;
            } else {
                $weak[] = $item;
            }
        }

        return [
            'total_knowledge_points' => $total,
            'average_mastery_level' => round($average, 4),
            'mastered_knowledge_points' => count($mastered),
            'good_knowledge_points' => count($good),
            'weak_knowledge_points' => count($weak),
            'weak_knowledge_points_list' => $weak,
            'details' => $masteryArray,
        ];
    }

    /**
     * 【新功能】获取知识点层级关系
     */
    private function getKnowledgePointHierarchy(string $kpCode): ?array
    {
        try {
            $kp = DB::table('knowledge_points')
                ->where('kp_code', $kpCode)
                ->first();

            if (!$kp) {
                return null;
            }

            return [
                'kp_code' => $kp->kp_code,
                'parent_kp_code' => $kp->parent_kp_code,
                'level' => $kp->level ?? 1,
            ];
        } catch (\Exception $e) {
            Log::warning('获取知识点层级关系失败', [
                'kp_code' => $kpCode,
                'error' => $e->getMessage(),
            ]);
            return null;
        }
    }

    /**
     * 【公司要求】计算父节点掌握度（所有子节点算术平均值）
     *
     * 公司要求：
     * 1. 查询结构：通过 knowledge_points 表获取知识点的层级关系（parent_kp_code）
     * 2. 平均值计算：如果一个知识点是父节点，它的掌握度不再从数据库直接读，
     *    而是实时计算其下所有子节点掌握度的算术平均数
     * 3. 多级递归：支持多级结构，能够从最底层逐级向上求平均
     *
     * 递归计算流程：
     * - 从最底层叶子节点开始，逐级向上计算每个父节点的掌握度
     * - 父节点掌握度 = 所有直接子节点掌握度的算术平均数
     *
     * @param string $studentId 学生ID
     * @param string $parentKpCode 父节点编码
     * @param int $maxDepth 最大递归深度（默认10级）
     * @return float 父节点掌握度（0.0-1.0）
     */
    public function calculateParentMastery(string $studentId, string $parentKpCode, int $maxDepth = 3): float
    {
        return $this->calculateParentMasteryRecursive($studentId, $parentKpCode, 1, $maxDepth);
    }

    /**
     * 【公司要求】递归计算父节点掌握度（多级结构支持）
     *
     * 多级递归计算流程：
     * 1. 获取父节点的所有直接子节点
     * 2. 对每个子节点：
     *    - 如果子节点也是父节点，递归计算其掌握度
     *    - 如果子节点是叶子节点，从 student_knowledge_mastery 表读取掌握度
     * 3. 计算所有子节点掌握度的算术平均数
     * 4. 返回父节点掌握度
     *
     * @param string $studentId 学生ID
     * @param string $parentKpCode 父节点编码
     * @param int $currentDepth 当前递归深度
     * @param int $maxDepth 最大递归深度（防止无限递归）
     * @return float 父节点掌握度
     */
    private function calculateParentMasteryRecursive(string $studentId, string $parentKpCode, int $currentDepth, int $maxDepth): float
    {
        try {
            // 【公司要求】防止无限递归
            if ($currentDepth > $maxDepth) {
                Log::warning('父节点掌握度计算达到最大递归深度', [
                    'student_id' => $studentId,
                    'parent_kp_code' => $parentKpCode,
                    'current_depth' => $currentDepth,
                    'max_depth' => $maxDepth
                ]);
                return 0.0;
            }

            // 【公司要求】查询结构：通过 knowledge_points 表获取知识点的层级关系（parent_kp_code）
            // 获取所有直接子节点
            $childKps = DB::table('knowledge_points')
                ->where('parent_kp_code', $parentKpCode)
                ->pluck('kp_code')
                ->toArray();

            if (empty($childKps)) {
                // 如果没有子节点，返回0
                Log::debug('父节点没有子节点，返回0', [
                    'student_id' => $studentId,
                    'parent_kp_code' => $parentKpCode
                ]);
                return 0.0;
            }

            // 【公司要求】多级递归：支持多级结构，能够从最底层逐级向上求平均
            // 获取所有子节点的掌握度（递归计算）
            $masteryLevels = [];
            foreach ($childKps as $childKpCode) {
                // 【公司要求】多级递归：递归计算子节点掌握度
                $childMastery = $this->calculateParentMasteryRecursive($studentId, $childKpCode, $currentDepth + 1, $maxDepth);

                // 如果子节点没有子节点，则从student_knowledge_mastery表读取
                if ($childMastery == 0.0) {
                    $mastery = DB::table('student_knowledge_mastery')
                        ->where('student_id', $studentId)
                        ->where('kp_code', $childKpCode)
                        ->value('mastery_level');

                    if ($mastery !== null) {
                        $masteryLevels[] = floatval($mastery);
                    }
                } else {
                    // 子节点有子节点，使用递归计算的结果
                    $masteryLevels[] = $childMastery;
                }
            }

            if (empty($masteryLevels)) {
                Log::warning('父节点所有子节点都没有掌握度数据', [
                    'student_id' => $studentId,
                    'parent_kp_code' => $parentKpCode,
                    'child_count' => count($childKps)
                ]);
                return 0.0;
            }

            // 【公司要求】平均值计算：父节点掌握度 = 所有子节点掌握度的算术平均数
            $averageMastery = array_sum($masteryLevels) / count($masteryLevels);
            $result = round($averageMastery, 4);

            Log::info('父节点掌握度计算完成', [
                'student_id' => $studentId,
                'parent_kp_code' => $parentKpCode,
                'child_count' => count($childKps),
                'mastery_count' => count($masteryLevels),
                'average_mastery' => $result,
                'child_masteries' => $masteryLevels,
                'current_depth' => $currentDepth
            ]);

            return $result;

        } catch (\Exception $e) {
            Log::error('计算父节点掌握度失败', [
                'student_id' => $studentId,
                'parent_kp_code' => $parentKpCode,
                'current_depth' => $currentDepth,
                'error' => $e->getMessage(),
            ]);
            return 0.0;
        }
    }

    /**
     * 【增强】获取学生所有知识点的掌握度概览（支持父节点计算）
     */
    public function getStudentMasteryOverviewWithHierarchy(string $studentId): array
    {
        $masteryList = DB::table('student_knowledge_mastery')
            ->where('student_id', $studentId)
            ->get();

        if ($masteryList->isEmpty()) {
            return [
                'total_knowledge_points' => 0,
                'average_mastery_level' => 0.0,
                'mastered_knowledge_points' => 0,
                'good_knowledge_points' => 0,
                'weak_knowledge_points' => 0,
                'weak_knowledge_points_list' => [],
                'details' => [],
                'parent_mastery_levels' => [], // 新增：父节点掌握度
            ];
        }

        $masteryArray = $masteryList->toArray();

        $total = count($masteryArray);
        $average = $masteryArray ? array_sum(array_column($masteryArray, 'mastery_level')) / $total : 0;

        $mastered = [];
        $good = [];
        $weak = [];

        foreach ($masteryArray as $item) {
            $level = floatval($item->mastery_level);
            if ($level >= self::MASTERY_THRESHOLD_MASTER) {
                $mastered[] = $item;
            } elseif ($level >= self::MASTERY_THRESHOLD_GOOD) {
                $good[] = $item;
            } else {
                $weak[] = $item;
            }
        }

        // 【新功能】计算父节点掌握度
        $parentMasteryLevels = [];
        $parentKpCodes = DB::table('knowledge_points')
            ->whereNotNull('parent_kp_code')
            ->distinct()
            ->pluck('parent_kp_code')
            ->toArray();

        foreach ($parentKpCodes as $parentKpCode) {
            $parentMastery = $this->calculateParentMastery($studentId, $parentKpCode);
            $parentMasteryLevels[$parentKpCode] = $parentMastery;
        }

        return [
            'total_knowledge_points' => $total,
            'average_mastery_level' => round($average, 4),
            'mastered_knowledge_points' => count($mastered),
            'good_knowledge_points' => count($good),
            'weak_knowledge_points' => count($weak),
            'weak_knowledge_points_list' => $weak,
            'details' => $masteryArray,
            'parent_mastery_levels' => $parentMasteryLevels, // 新增：父节点掌握度
        ];
    }
}
