<?php

namespace Tests\Feature\Api\V1\Workflows;

use App\Models\Role;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Event;
use Tests\Concerns\CreatesApiTestUsers;
use Tests\TestCase;

/**
 * End-to-end "happy path" for onboarding a family:
 *   1. An administrator signs in.
 *   2. They create a parent account.
 *   3. They register a student under that parent.
 *   4. They enrol the student into a class section.
 *   5. They move the student's enrollment status to "enrolled".
 *
 * Each step uses the real HTTP API so the controllers, form requests,
 * services and persistence layers are all exercised together.
 */
class StudentEnrollmentWorkflowTest extends TestCase
{
    use CreatesApiTestUsers;
    use RefreshDatabase;

    private const SCHOOL_YEAR = '2025-2026';
    private const SEMESTER = 'Fall';

    public function test_admin_creates_account_adds_student_and_enrolls_them(): void
    {
        Event::fake();

        // 1. Administrator is authenticated (also seeds roles + active school year).
        $admin = $this->actingAsApiAdministrator();
        $parentRoleId = (int) Role::query()->where('name', 'parent')->value('id');
        $this->assertGreaterThan(0, $parentRoleId);

        // 2. Create a brand-new parent account.
        $createParent = $this->postJson('/api/v1/users', [
            'firstname' => 'Khadija',
            'lastname' => 'Saleh',
            'gender' => 'Female',
            'cellphone' => '5551234567',
            'email' => 'khadija.saleh@example.test',
            'address_street' => '42 Cedar Lane',
            'city' => 'Brooklyn',
            'state' => 'NY',
            'zip' => '11201',
            'accept_school_policy' => true,
            'role_id' => $parentRoleId,
            'password' => 'secret123',
            'semester' => self::SEMESTER,
            'school_year' => self::SCHOOL_YEAR,
            'user_type' => 'primary',
            'status' => 'Active',
            'is_verified' => true,
        ]);

        $createParent->assertStatus(201)->assertJsonPath('ok', true);
        $parentId = (int) $createParent->json('user_id');
        $this->assertGreaterThan(0, $parentId);

        $this->assertDatabaseHas('users', [
            'id' => $parentId,
            'email' => 'khadija.saleh@example.test',
        ]);
        $this->assertDatabaseHas('user_roles', [
            'user_id' => $parentId,
            'role_id' => $parentRoleId,
        ]);

        // 3. Register the parent's first student (triggers the first-child
        //    emergency-contact branch in StudentController@store).
        $createStudent = $this->postJson('/api/v1/students', [
            'firstname' => 'Yusuf',
            'lastname' => 'Saleh',
            'dob' => '2015-05-10',
            'age' => 10,
            'gender' => 'Male',
            'is_active' => true,
            'registration_grade' => '5',
            'is_new' => true,
            'photo_consent' => true,
            'parent_id' => $parentId,
            'registration_date' => '2025-09-01',
            'tuition_paid' => false,
            'semester' => self::SEMESTER,
            'year_of_registration' => 2025,
            'school_year' => self::SCHOOL_YEAR,
            // first-child contact details:
            'name' => 'Khadija Saleh',
            'cellphone' => '5551234567',
            'email' => 'khadija.saleh@example.test',
            'relation' => 'Mother',
        ]);

        $createStudent->assertStatus(201)->assertJsonPath('ok', true);
        $studentId = (int) $createStudent->json('student.id');
        $this->assertGreaterThan(0, $studentId);

        $this->assertDatabaseHas('students', [
            'id' => $studentId,
            'parent_id' => $parentId,
            'firstname' => 'Yusuf',
            'lastname' => 'Saleh',
        ]);
        $this->assertDatabaseHas('emergency_contacts', [
            'parent_id' => $parentId,
            'emergency_contact_name' => 'Khadija Saleh',
            'relation' => 'Mother',
        ]);

        // 4. Enrol the student into a class section.
        $classSectionId = $this->seedClassSection();
        $enroll = $this->postJson('/api/v1/students/assign-class', [
            'student_id' => $studentId,
            'class_section_id' => [$classSectionId],
        ]);

        $enroll->assertOk()->assertJsonPath('ok', true);
        $this->assertDatabaseHas('student_class', [
            'student_id' => $studentId,
            'class_section_id' => $classSectionId,
            'school_year' => self::SCHOOL_YEAR,
        ]);

        // The class roster endpoint reflects the assignment.
        $classes = $this->getJson('/api/v1/students/' . $studentId . '/classes');
        $classes->assertOk()->assertJsonPath('ok', true);
        $assignedIds = array_column($classes->json('classes'), 'class_section_id');
        $this->assertContains($classSectionId, $assignedIds);

        // 5. Move the student to "enrolled" via the enrollment workflow.
        $statusResponse = $this->post(
            '/api/v1/administrator/enrollment-withdrawal/update-statuses?' .
            http_build_query(['enrollment_status' => [$studentId => 'enrolled']]),
            []
        );

        $statusResponse->assertOk();
        $this->assertDatabaseHas('enrollments', [
            'student_id' => $studentId,
            'parent_id' => $parentId,
            'school_year' => self::SCHOOL_YEAR,
            'semester' => self::SEMESTER,
            'enrollment_status' => 'enrolled',
            'admission_status' => 'accepted',
        ]);
    }

    public function test_admin_can_withdraw_an_enrolled_student(): void
    {
        Event::fake();

        $this->actingAsApiAdministrator();
        $parentRoleId = (int) Role::query()->where('name', 'parent')->value('id');

        $parentId = (int) $this->postJson('/api/v1/users', [
            'firstname' => 'Omar',
            'lastname' => 'Hadid',
            'gender' => 'Male',
            'cellphone' => '5559876543',
            'email' => 'omar.hadid@example.test',
            'address_street' => '7 Maple Ct',
            'city' => 'Queens',
            'state' => 'NY',
            'zip' => '11367',
            'accept_school_policy' => true,
            'role_id' => $parentRoleId,
            'password' => 'secret123',
            'semester' => self::SEMESTER,
            'school_year' => self::SCHOOL_YEAR,
            'user_type' => 'primary',
            'status' => 'Active',
            'is_verified' => true,
        ])->json('user_id');

        $studentId = (int) $this->postJson('/api/v1/students', [
            'firstname' => 'Layla',
            'lastname' => 'Hadid',
            'gender' => 'Female',
            'is_active' => true,
            'registration_grade' => '2',
            'parent_id' => $parentId,
            'semester' => self::SEMESTER,
            'school_year' => self::SCHOOL_YEAR,
            'name' => 'Omar Hadid',
            'cellphone' => '5559876543',
            'email' => 'omar.hadid@example.test',
            'relation' => 'Father',
        ])->json('student.id');

        // First enrol...
        $this->post(
            '/api/v1/administrator/enrollment-withdrawal/update-statuses?' .
            http_build_query(['enrollment_status' => [$studentId => 'enrolled']]),
            []
        )->assertOk();

        // ...then withdraw.
        $this->post(
            '/api/v1/administrator/enrollment-withdrawal/update-statuses?' .
            http_build_query(['enrollment_status' => [$studentId => 'withdrawn']]),
            []
        )->assertOk();

        $this->assertDatabaseHas('enrollments', [
            'student_id' => $studentId,
            'parent_id' => $parentId,
            'school_year' => self::SCHOOL_YEAR,
            'enrollment_status' => 'withdrawn',
        ]);
    }

    public function test_creating_a_user_requires_a_valid_role(): void
    {
        $this->actingAsApiAdministrator();

        $this->postJson('/api/v1/users', [
            'firstname' => 'No',
            'lastname' => 'Role',
            'cellphone' => '5550001111',
            'email' => 'no.role@example.test',
            'address_street' => '1 Test St',
            'city' => 'Town',
            'state' => 'NY',
            'zip' => '10001',
            'accept_school_policy' => true,
            'role_id' => 999999,
            'password' => 'secret123',
            'semester' => self::SEMESTER,
        ])->assertStatus(422)
            ->assertJsonValidationErrors(['role_id']);
    }

    public function test_student_registration_requires_first_child_contact_details(): void
    {
        $this->actingAsApiAdministrator();
        $parentRoleId = (int) Role::query()->where('name', 'parent')->value('id');

        $parentId = (int) $this->postJson('/api/v1/users', [
            'firstname' => 'First',
            'lastname' => 'Parent',
            'gender' => 'Female',
            'cellphone' => '5552223333',
            'email' => 'first.parent@example.test',
            'address_street' => '9 Birch Rd',
            'city' => 'Bronx',
            'state' => 'NY',
            'zip' => '10451',
            'accept_school_policy' => true,
            'role_id' => $parentRoleId,
            'password' => 'secret123',
            'semester' => self::SEMESTER,
            'school_year' => self::SCHOOL_YEAR,
        ])->json('user_id');

        // First student for a parent must include name/cellphone/email.
        $this->postJson('/api/v1/students', [
            'firstname' => 'Solo',
            'lastname' => 'Child',
            'parent_id' => $parentId,
            'school_year' => self::SCHOOL_YEAR,
        ])->assertStatus(422)
            ->assertJsonValidationErrors(['name', 'cellphone', 'email']);
    }

    private function seedClassSection(int $classSectionId = 701, int $classId = 1): int
    {
        DB::table('classSection')->insert([
            'class_id' => $classId,
            'class_section_id' => $classSectionId,
            'class_section_name' => '5A',
            'semester' => self::SEMESTER,
            'school_year' => self::SCHOOL_YEAR,
        ]);

        return $classSectionId;
    }
}
