<?php

namespace Tests\Unit\Services\Promotions;

use App\Models\StudentPromotionRecord;
use App\Services\Promotions\LevelProgressionService;
use App\Services\Promotions\PromotionAuditService;
use App\Services\Promotions\PromotionEligibilityService;
use App\Services\Promotions\PromotionStatusService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class PromotionEligibilityServiceTest extends TestCase
{
    use RefreshDatabase;

    public function test_passing_student_becomes_awaiting_parent_enrollment(): void
    {
        $this->seedConfig();
        $studentId = $this->seedStudent();
        $this->seedClassChain();
        $this->seedAssignment($studentId, 701, '2025-2026');
        $this->seedDecision($studentId, '2025-2026', 77.5, 'promoted');

        $service = $this->makeService();
        $record = $service->evaluateStudent($studentId, '2025-2026', 1);

        $this->assertNotNull($record);
        $this->assertSame(StudentPromotionRecord::STATUS_AWAITING_PARENT, $record->promotion_status);
        $this->assertTrue((bool) $record->passed_current_level);
        $this->assertSame(77.5, (float) $record->final_average);
        $this->assertSame('Grade 3', $record->current_level_name);
        $this->assertSame('Grade 4', $record->promoted_level_name);
        $this->assertSame('2026-2027', $record->next_school_year);
    }

    public function test_failing_student_becomes_repeated(): void
    {
        $this->seedConfig();
        $studentId = $this->seedStudent();
        $this->seedClassChain();
        $this->seedAssignment($studentId, 701, '2025-2026');
        $this->seedDecision($studentId, '2025-2026', 52.5, 'retained');

        $service = $this->makeService();
        $record = $service->evaluateStudent($studentId, '2025-2026', 1);

        $this->assertNotNull($record);
        $this->assertSame(StudentPromotionRecord::STATUS_REPEATED, $record->promotion_status);
        $this->assertFalse((bool) $record->passed_current_level);
    }

    public function test_missing_scores_marks_on_hold(): void
    {
        $this->seedConfig();
        $studentId = $this->seedStudent();
        $this->seedClassChain();
        $this->seedAssignment($studentId, 701, '2025-2026');

        $service = $this->makeService();
        $record = $service->evaluateStudent($studentId, '2025-2026', 1);

        $this->assertNotNull($record);
        $this->assertSame(StudentPromotionRecord::STATUS_ON_HOLD, $record->promotion_status);
        $this->assertNull($record->passed_current_level);
    }

    public function test_terminal_level_is_graduated(): void
    {
        $this->seedConfig();
        $studentId = $this->seedStudent();
        $this->seedClassChain();
        $this->seedAssignment($studentId, 901, '2025-2026');
        $this->seedDecision($studentId, '2025-2026', 90.0, 'promoted');

        // Mark Youth as terminal
        \App\Models\LevelProgression::query()->where('current_level_name', 'Youth')->update(['is_terminal' => 1]);

        $service = $this->makeService();
        $record = $service->evaluateStudent($studentId, '2025-2026', 1);

        $this->assertNotNull($record);
        $this->assertSame(StudentPromotionRecord::STATUS_GRADUATED, $record->promotion_status);
    }

    public function test_audit_log_records_status_changes(): void
    {
        $this->seedConfig();
        $studentId = $this->seedStudent();
        $this->seedClassChain();
        $this->seedAssignment($studentId, 701, '2025-2026');
        $this->seedDecision($studentId, '2025-2026', 80.0, 'promoted');

        $service = $this->makeService();
        $service->evaluateStudent($studentId, '2025-2026', 99);

        $this->assertDatabaseHas('promotion_audit_log', [
            'student_id' => $studentId,
            'action_type' => 'eligibility_evaluated',
            'user_id' => 99,
        ]);
        $this->assertDatabaseHas('promotion_audit_log', [
            'student_id' => $studentId,
            'action_type' => 'record_created',
        ]);
    }

    private function makeService(): PromotionEligibilityService
    {
        $progression = new LevelProgressionService();
        $audit = new PromotionAuditService();
        $status = new PromotionStatusService($audit);
        return new PromotionEligibilityService($progression, $status, $audit);
    }

    private function seedConfig(): void
    {
        DB::table('configuration')->insert([
            ['config_key' => 'school_year', 'config_value' => '2025-2026'],
            ['config_key' => 'semester', 'config_value' => 'Fall'],
        ]);
    }

    private function seedStudent(int $parentId = 5): int
    {
        return DB::table('students')->insertGetId([
            'school_id' => 'S-100',
            'firstname' => 'Sara',
            'lastname' => 'Test',
            'parent_id' => $parentId,
            'year_of_registration' => '2025',
            'school_year' => '2025-2026',
            'semester' => 'Fall',
        ]);
    }

    private function seedClassChain(): void
    {
        DB::table('classes')->insert([
            ['id' => 7, 'class_name' => 'Grade 3', 'school_year' => '2025-2026', 'semester' => 'Fall', 'capacity' => 30],
            ['id' => 8, 'class_name' => 'Grade 4', 'school_year' => '2025-2026', 'semester' => 'Fall', 'capacity' => 30],
            ['id' => 13, 'class_name' => 'Youth', 'school_year' => '2025-2026', 'semester' => 'Fall', 'capacity' => 30],
        ]);
        DB::table('classSection')->insert([
            ['class_id' => 7, 'class_section_id' => 701, 'class_section_name' => '3A'],
            ['class_id' => 13, 'class_section_id' => 901, 'class_section_name' => 'Youth A'],
        ]);
    }

    private function seedAssignment(int $studentId, int $classSectionId, string $schoolYear): void
    {
        DB::table('student_class')->insert([
            'student_id' => $studentId,
            'class_section_id' => $classSectionId,
            'school_year' => $schoolYear,
            'semester' => 'Fall',
            'is_event_only' => 0,
        ]);
    }

    private function seedDecision(int $studentId, string $schoolYear, float $score, string $decision): void
    {
        DB::table('student_decisions')->insert([
            'student_id' => $studentId,
            'school_year' => $schoolYear,
            'class_section_name' => '3A',
            'year_score' => $score,
            'decision' => $decision,
            'source' => 'manual',
            'notes' => 'Seeded test decision',
            'created_at' => now(),
            'updated_at' => now(),
        ]);
    }
}