<?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,
                'confidence' => 0.0,
                'trend' => 'insufficient',
                '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'],
            'confidence' => $masteryData['confidence'],
            'trend' => $masteryData['trend'],
        ]);

        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.5; // 默认0.5

        // 统计正确和错误次数
        $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
            ]);
        }

        // 计算平均变化（避免单次考试影响过大）
        $averageChange = $totalAttempts > 0 ? $totalChange / $totalAttempts : 0.0;

        // 应用权重调整（避免单次考试变化过大）
        $weightedChange = $averageChange * min($totalAttempts / 10.0, 1.0); // 最多10次考试达到满权重

        // 新掌握度 = 旧掌握度 + 加权变化
        $newMastery = $oldMastery + $weightedChange;

        // 边界限制：0.0 ~ 1.0
        $newMastery = max(0.0, min(1.0, $newMastery));

        // 计算置信度（基于答题次数）
        $confidence = $this->calculateConfidence($attempts);

        // 判断趋势
        $trend = $this->determineTrend($attempts);

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

        return [
            'mastery' => round($newMastery, 4),
            'confidence' => round($confidence, 4),
            'trend' => $trend,
            'total_attempts' => $totalAttempts,
            'correct_attempts' => $correctAttempts,
            'accuracy_rate' => round(($correctAttempts / $totalAttempts) * 100, 2),
            'old_mastery' => $oldMastery,
            'change' => round($weightedChange, 4),
            'details' => [
                'exam_base_difficulty' => $examBaseDifficulty,
                'total_change' => round($totalChange, 4),
                'average_change' => round($averageChange, 4),
                'weighted_change' => round($weightedChange, 4),
            ],
        ];
    }

    /**
     * 难度映射：将0.0-1.0的浮点数难度映射为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级
        }
    }

    /**
     * 根据难度关系计算权重变化
     */
    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 calculateConfidence(array $attempts): float
    {
        if (empty($attempts)) {
            return 0.0;
        }

        $totalAttempts = count($attempts);

        // 基于答题次数的置信度：答题越多，置信度越高
        // 5次以下线性增长，5次以上增长放缓
        if ($totalAttempts < 5) {
            $baseConfidence = $totalAttempts / 5.0;
        } else {
            $baseConfidence = 0.5 + (1.0 - 0.5) * (1 - exp(-($totalAttempts - 5) / 10));
        }

        // 计算正确率
        $correctAttempts = count(array_filter($attempts, fn($a) => $a['is_correct']));
        $accuracyRate = $correctAttempts / $totalAttempts;

        // 正确率也影响置信度
        $accuracyFactor = 0.5 + $accuracyRate * 0.5;

        // 综合置信度
        $confidence = $baseConfidence * $accuracyFactor;

        return min($confidence, 1.0);
    }

    /**
     * 判断学习趋势
     */
    private function determineTrend(array $attempts): string
    {
        if (count($attempts) < 3) {
            return 'insufficient'; // 数据不足
        }

        // 按时间排序
        usort($attempts, function($a, $b) {
            $timeA = strtotime($a['completed_at'] ?? $a['created_at'] ?? 0);
            $timeB = strtotime($b['completed_at'] ?? $b['created_at'] ?? 0);
            return $timeA <=> $timeB;
        });

        // 分为前后两部分
        $midPoint = intdiv(count($attempts), 2);
        $firstHalf = array_slice($attempts, 0, $midPoint);
        $secondHalf = array_slice($attempts, $midPoint);

        // 计算前半部分和后半部分的正确率
        $firstHalfCorrect = count(array_filter($firstHalf, fn($a) => $a['is_correct']));
        $firstHalfAccuracy = $firstHalfCorrect / count($firstHalf);

        $secondHalfCorrect = count(array_filter($secondHalf, fn($a) => $a['is_correct']));
        $secondHalfAccuracy = $secondHalfCorrect / count($secondHalf);

        $improvement = $secondHalfAccuracy - $firstHalfAccuracy;

        if ($improvement > 0.1) {
            return 'improving'; // 提升
        } elseif ($improvement < -0.1) {
            return 'declining'; // 下降
        } else {
            return 'stable'; // 稳定
        }
    }

    /**
     * 获取学生的答题记录
     */
    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) {
            $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, // 步骤分数本身就是满分
                '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) {
                $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,
                    '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;
        }
    }

    /**
     * 【新功能】计算父节点掌握度（子节点平均值）
     */
    public function calculateParentMastery(string $studentId, string $parentKpCode): float
    {
        try {
            // 获取所有子节点
            $childKps = DB::table('knowledge_points')
                ->where('parent_kp_code', $parentKpCode)
                ->pluck('kp_code')
                ->toArray();

            if (empty($childKps)) {
                // 如果没有子节点，返回0
                return 0.0;
            }

            // 获取所有子节点的掌握度
            $masteryLevels = [];
            foreach ($childKps as $childKpCode) {
                $mastery = DB::table('student_knowledge_mastery')
                    ->where('student_id', $studentId)
                    ->where('kp_code', $childKpCode)
                    ->value('mastery_level');

                if ($mastery !== null) {
                    $masteryLevels[] = floatval($mastery);
                }
            }

            if (empty($masteryLevels)) {
                return 0.0;
            }

            // 计算算术平均数
            $averageMastery = array_sum($masteryLevels) / count($masteryLevels);

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

            return round($averageMastery, 4);

        } catch (\Exception $e) {
            Log::error('计算父节点掌握度失败', [
                'student_id' => $studentId,
                'parent_kp_code' => $parentKpCode,
                '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, // 新增：父节点掌握度
        ];
    }
}
