<?php

namespace App\Services\Scores;

class ScorePredictorRiskService
{
    public function normalCdf(float $z): float
    {
        $t = 1 / (1 + 0.3275911 * abs($z));
        $a1 = 0.254829592;
        $a2 = -0.284496736;
        $a3 = 1.421413741;
        $a4 = -1.453152027;
        $a5 = 1.061405429;
        $erf = 1 - ((((($a5 * $t + $a4) * $t + $a3) * $t + $a2) * $t + $a1) * $t) * exp(-$z * $z);
        $sign = $z < 0 ? -1 : 1;
        return 0.5 * (1 + $sign * $erf);
    }

    public function evaluate(array $students, float $mean, float $std, float $trophyThreshold, float $targetLow): array
    {
        $results = [];

        foreach ($students as $student) {
            $fallScore = is_numeric($student['fall_score'] ?? null) ? (float) $student['fall_score'] : null;
            $springScore = is_numeric($student['spring_score'] ?? null) ? (float) $student['spring_score'] : null;

            $scoreParts = array_values(array_filter([$fallScore, $springScore], static fn ($v) => $v !== null));
            $finalAvg = !empty($scoreParts) ? round(array_sum($scoreParts) / count($scoreParts), 1) : null;

            $waitingForSpring = ($fallScore !== null && $springScore === null);

            $requiredHigh = $waitingForSpring ? round(2 * $trophyThreshold - $fallScore, 2) : null;
            $requiredLow = $waitingForSpring ? round(2 * $targetLow - $fallScore, 2) : null;

            $zHigh = ($requiredHigh !== null && $std > 0) ? ($requiredHigh - $mean) / $std : null;
            $zLow = ($requiredLow !== null && $std > 0) ? ($requiredLow - $mean) / $std : null;

            $probHigh = $zHigh !== null ? 1 - $this->normalCdf($zHigh) : null;
            $probLow = $zLow !== null ? $this->normalCdf($zLow) : null;

            $riskHigh = match (true) {
                $finalAvg !== null && !$waitingForSpring && $finalAvg >= $trophyThreshold => 'Achieved Trophy',
                $finalAvg !== null && !$waitingForSpring => 'Unreachable',
                $requiredHigh !== null && $requiredHigh > 100 => 'Unreachable',
                is_null($probHigh) => 'New student',
                $probHigh >= 0.66 => 'Low',
                $probHigh >= 0.33 => 'Medium',
                default => 'High',
            };

            $riskLow = match (true) {
                $finalAvg !== null && !$waitingForSpring && $finalAvg < $targetLow => 'High',
                $finalAvg !== null && !$waitingForSpring => 'Low',
                $requiredLow !== null && $requiredLow > 100 => 'Unlikely to pass',
                is_null($probLow) => 'New student',
                $probLow >= 0.66 => 'High',
                $probLow >= 0.33 => 'Medium',
                default => 'Low',
            };

            if ($finalAvg === null) {
                $status = 'Undetermined';
            } elseif ($waitingForSpring) {
                $status = in_array($riskLow, ['High', 'Unlikely to pass'], true) ? 'May fail' : 'Can pass';
            } else {
                $status = $finalAvg >= 60 ? 'Passed' : 'Failed';
            }

            $results[] = [
                'student_id' => (int) ($student['student_id'] ?? 0),
                'school_id' => $student['school_id'] ?? null,
                'firstname' => $student['firstname'] ?? null,
                'lastname' => $student['lastname'] ?? null,
                'class_section_id' => $student['class_section_id'] ?? null,
                'fall_score' => $student['fall_score'] ?? 'N/A',
                'spring_score' => $student['spring_score'] ?? 'N/A',
                'final_average' => $finalAvg ?? 'N/A',
                'required_spring_score' => $requiredHigh ?? 'N/A',
                'winning_risk' => $riskHigh,
                'required_spring_to_pass' => $requiredLow ?? 'N/A',
                'failure_risk' => $riskLow,
                'status' => $status,
                'trophy_awarded' => ($riskHigh === 'Achieved Trophy'),
                'trophy_reason' => ($riskHigh === 'Achieved Trophy') ? 'threshold' : '',
            ];
        }

        return $this->applyTop3Trophy($results);
    }

    private function applyTop3Trophy(array $results): array
    {
        $bySection = [];
        $hasThresholdTrophy = [];

        foreach ($results as $idx => $row) {
            $cid = (string) ($row['class_section_id'] ?? '');
            $bySection[$cid][] = $idx;
            if (($row['trophy_reason'] ?? '') === 'threshold') {
                $hasThresholdTrophy[$cid] = true;
            }
        }

        foreach ($bySection as $cid => $indices) {
            if (!empty($hasThresholdTrophy[$cid])) {
                continue;
            }
            $candidates = [];
            foreach ($indices as $idx) {
                $val = $results[$idx]['final_average'] ?? null;
                if (!is_numeric($val)) {
                    $fall = $results[$idx]['fall_score'] ?? null;
                    $spring = $results[$idx]['spring_score'] ?? null;
                    $parts = [];
                    if (is_numeric($fall)) $parts[] = (float) $fall;
                    if (is_numeric($spring)) $parts[] = (float) $spring;
                    if (!empty($parts)) {
                        $val = array_sum($parts) / count($parts);
                    }
                }
                if (is_numeric($val)) {
                    $candidates[] = ['idx' => $idx, 'avg' => (float) $val];
                }
            }
            if (empty($candidates)) {
                continue;
            }
            usort($candidates, static fn ($a, $b) => $b['avg'] <=> $a['avg']);
            $top = array_slice($candidates, 0, 3);
            foreach ($top as $entry) {
                $i = $entry['idx'];
                $results[$i]['trophy_awarded'] = true;
                $results[$i]['trophy_reason'] = 'top3';
                $results[$i]['winning_risk'] = 'Top 3 Trophy';
            }
        }

        usort($results, static fn ($a, $b) => ($b['final_average'] ?? 0) <=> ($a['final_average'] ?? 0));

        return $results;
    }
}
