<?php

namespace Tests\Unit\Services;

use App\Models\ClassSection;
use App\Models\Configuration;
use App\Models\SemesterScore;
use App\Models\Student;
use App\Models\StudentClass;
use App\Services\Administrator\Trophy\TrophyReportService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class TrophyReportServiceTest extends TestCase
{
    use RefreshDatabase;

    protected TrophyReportService $service;

    protected string $schoolYear = '2025-2026';

    protected ClassSection $section;

    protected function setUp(): void
    {
        parent::setUp();

        $this->service = app(TrophyReportService::class);

        Configuration::query()->create([
            'config_key' => 'school_year',
            'config_value' => $this->schoolYear,
        ]);

        $this->section = ClassSection::factory()->create([
            'class_section_id' => 401,
            'class_section_name' => 'Grade 4A',
        ]);
    }

    public function test_calculate_threshold_enforces_minimum_of_three_winners(): void
    {
        $result = $this->service->calculateThreshold([50, 60, 70, 80, 90], 95.0);

        $this->assertSame('min3_reduced', $result['method']);
        $this->assertSame(3, $result['winners']);
        $this->assertSame(70.0, $result['threshold']);
    }

    public function test_projection_marks_students_without_fall_scores_as_not_projected(): void
    {
        $first = $this->enrollStudent('Amina', 'Top', 'Female', 'S-1', 98.0);
        $second = $this->enrollStudent('Bilal', 'Strong', 'Male', 'S-2', 95.0);
        $third = $this->enrollStudent('Celine', 'Good', 'Female', 'S-3', 90.0);
        $fourth = $this->enrollStudent('David', 'Missing', 'Male', 'S-4', null);

        $payload = $this->service->projection($this->schoolYear, 75.0);

        $this->assertSame($this->schoolYear, $payload['selected_year']);
        $this->assertSame(75.0, $payload['selected_percentile']);
        $this->assertSame([$this->schoolYear], $payload['years']);
        $this->assertCount(1, $payload['class_results']);

        $classResult = $payload['class_results'][0];

        $this->assertSame(4, $classResult['student_count']);
        $this->assertSame(3, $classResult['scored_count']);
        $this->assertSame(3, $classResult['trophy_count']);
        $this->assertSame(90.0, $classResult['threshold']);
        $this->assertSame(4, $payload['summary']['students']);
        $this->assertSame(3, $payload['summary']['trophies']);

        $students = collect($classResult['students'])->keyBy('student_id');

        $this->assertTrue($students[$first->id]['projected_trophy']);
        $this->assertTrue($students[$second->id]['projected_trophy']);
        $this->assertTrue($students[$third->id]['projected_trophy']);
        $this->assertFalse($students[$fourth->id]['projected_trophy']);
        $this->assertNull($students[$fourth->id]['fall_score']);
    }

    public function test_final_report_classifies_confirmed_surprise_and_missed_winners(): void
    {
        $this->enrollStudent('Adam', 'Confirmed', 'Male', 'S-10', 99.0, 97.0);
        $this->enrollStudent('Basma', 'Confirmed', 'Female', 'S-11', 95.0, 93.0);
        $missed = $this->enrollStudent('Cyrus', 'Missed', 'Male', 'S-12', 92.0, 68.0);
        $surprise = $this->enrollStudent('Dina', 'Surprise', 'Female', 'S-13', 88.0, 100.0);
        $this->enrollStudent('Evan', 'None', 'Male', 'S-14', 70.0, 50.0);

        $payload = $this->service->final($this->schoolYear, 75.0);

        $this->assertCount(1, $payload['class_results']);
        $this->assertSame(3, $payload['summary']['predicted']);
        $this->assertSame(3, $payload['summary']['actual']);
        $this->assertSame(2, $payload['summary']['confirmed']);
        $this->assertSame(1, $payload['summary']['surprises']);
        $this->assertSame(1, $payload['summary']['missed']);
        $this->assertSame(67.0, $payload['summary']['accuracy']);
        $this->assertSame(3, $payload['winner_gender_summary']['total']);
        $this->assertSame(1, $payload['winner_gender_summary']['boys']);
        $this->assertSame(2, $payload['winner_gender_summary']['girls']);
        $this->assertCount(3, $payload['winner_stickers']);

        $classResult = $payload['class_results'][0];

        $this->assertSame(92.0, $classResult['fall_threshold']);
        $this->assertSame(94.0, $classResult['year_threshold']);
        $this->assertSame(2, $classResult['confirmed']);
        $this->assertSame(1, $classResult['surprises']);
        $this->assertSame(1, $classResult['missed']);
        $this->assertSame(67.0, $classResult['accuracy']);

        $students = collect($classResult['students'])->keyBy('student_id');

        $this->assertSame('missed', $students[$missed->id]['status']);
        $this->assertTrue($students[$missed->id]['predicted']);
        $this->assertFalse($students[$missed->id]['actual']);

        $this->assertSame('surprise', $students[$surprise->id]['status']);
        $this->assertFalse($students[$surprise->id]['predicted']);
        $this->assertTrue($students[$surprise->id]['actual']);
    }

    private function enrollStudent(
        string $firstname,
        string $lastname,
        string $gender,
        string $schoolId,
        ?float $fallScore,
        ?float $springScore = null
    ): Student {
        $student = Student::factory()->create([
            'firstname' => $firstname,
            'lastname' => $lastname,
            'gender' => $gender,
            'school_id' => $schoolId,
            'school_year' => $this->schoolYear,
            'is_active' => 1,
        ]);

        StudentClass::query()->create([
            'student_id' => $student->id,
            'school_id' => null,
            'class_section_id' => $this->section->class_section_id,
            'school_year' => $this->schoolYear,
            'semester' => 'Fall',
            'is_event_only' => 0,
        ]);

        if ($fallScore !== null) {
            SemesterScore::query()->create([
                'student_id' => $student->id,
                'school_id' => $schoolId,
                'class_section_id' => $this->section->class_section_id,
                'semester_score' => $fallScore,
                'semester' => 'Fall',
                'school_year' => $this->schoolYear,
            ]);
        }

        if ($springScore !== null) {
            SemesterScore::query()->create([
                'student_id' => $student->id,
                'school_id' => $schoolId,
                'class_section_id' => $this->section->class_section_id,
                'semester_score' => $springScore,
                'semester' => 'Spring',
                'school_year' => $this->schoolYear,
            ]);
        }

        return $student;
    }
}
