<?php

namespace Tests\Feature\Api\V1\PrintRequests;

use App\Models\PrintRequest;
use App\Models\Role;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\DB;
use Tests\TestCase;

class PrintRequestsWorkflowTest extends TestCase
{
    use RefreshDatabase;

    public function test_teacher_can_create_hand_copy_request_and_admin_can_advance_statuses(): void
    {
        $teacher = $this->createUserWithRole('teacher');
        $admin = $this->createUserWithRole('administrator');
        $classSectionId = $this->seedClassSection();

        $this->actingAs($teacher, 'api');
        $createResponse = $this->postJson('/api/v1/print-requests/hand-copy', [
            'class_id' => $classSectionId,
            'num_copies' => 3,
            'required_by' => '2026-09-13 11:00:00',
            'pickup_method' => 'Self Pickup',
        ]);

        $createResponse
            ->assertCreated()
            ->assertJsonPath('status', true)
            ->assertJsonPath('data.print_request.status', 'not_assigned');

        $requestId = (int) $createResponse->json('data.print_request.id');
        $this->assertDatabaseHas('print_requests', [
            'id' => $requestId,
            'teacher_id' => $teacher->id,
            'class_id' => $classSectionId,
            'file_path' => '',
            'num_copies' => 3,
            'status' => 'not_assigned',
        ]);

        $this->actingAs($admin, 'api');
        foreach (['assigned', 'done', 'delivered'] as $status) {
            $this->patchJson("/api/v1/print-requests/{$requestId}/status", [
                'status' => $status,
            ])
                ->assertOk()
                ->assertJsonPath('status', true)
                ->assertJsonPath('data.print_request.status', $status);
        }

        $this->assertDatabaseHas('print_requests', [
            'id' => $requestId,
            'admin_id' => $admin->id,
            'status' => 'delivered',
        ]);
    }

    public function test_admin_status_rejects_invalid_transition(): void
    {
        $teacher = $this->createUserWithRole('teacher');
        $admin = $this->createUserWithRole('administrator');
        $request = $this->seedPrintRequest($teacher, ['status' => 'delivered']);

        $this->actingAs($admin, 'api');
        $this->patchJson("/api/v1/print-requests/{$request->id}/status", [
            'status' => 'assigned',
        ])
            ->assertUnprocessable()
            ->assertJsonPath('status', false);

        $this->assertDatabaseHas('print_requests', [
            'id' => $request->id,
            'status' => 'delivered',
        ]);
    }

    public function test_teacher_routes_reject_non_teacher_roles_and_admin_routes_reject_teacher_roles(): void
    {
        $parent = $this->createUserWithRole('parent');
        $teacher = $this->createUserWithRole('teacher');

        $this->actingAs($parent, 'api');
        $this->getJson('/api/v1/print-requests/teacher')
            ->assertForbidden();

        $this->actingAs($teacher, 'api');
        $this->getJson('/api/v1/print-requests/admin')
            ->assertForbidden();
    }

    public function test_teacher_cannot_update_delete_or_download_another_teachers_request(): void
    {
        $owner = $this->createUserWithRole('teacher');
        $intruder = $this->createUserWithRole('teacher');
        $request = $this->seedPrintRequest($owner, [
            'status' => 'not_assigned',
            'file_path' => 'missing-file.pdf',
        ]);

        $this->actingAs($intruder, 'api');

        $this->patchJson("/api/v1/print-requests/{$request->id}", [
            'num_copies' => 8,
            'required_by' => '2026-09-20 11:00:00',
            'pickup_method' => 'Self Pickup',
        ])->assertForbidden();

        $this->deleteJson("/api/v1/print-requests/{$request->id}")
            ->assertForbidden();

        $this->getJson("/api/v1/print-requests/{$request->id}/file")
            ->assertForbidden();

        $this->assertDatabaseHas('print_requests', [
            'id' => $request->id,
            'teacher_id' => $owner->id,
            'num_copies' => 1,
        ]);
    }

    public function test_teacher_cannot_delete_request_after_processing_has_started(): void
    {
        $teacher = $this->createUserWithRole('teacher');
        $request = $this->seedPrintRequest($teacher, ['status' => 'done']);

        $this->actingAs($teacher, 'api');
        $this->deleteJson("/api/v1/print-requests/{$request->id}")
            ->assertBadRequest()
            ->assertJsonPath('status', false);

        $this->assertDatabaseHas('print_requests', [
            'id' => $request->id,
            'status' => 'done',
        ]);
    }

    public function test_upload_requires_valid_file_and_page_selection_format(): void
    {
        $teacher = $this->createUserWithRole('teacher');
        $classSectionId = $this->seedClassSection();

        $this->actingAs($teacher, 'api');
        $response = $this->post('/api/v1/print-requests', [
            'file' => UploadedFile::fake()->create('lesson.pdf', 12, 'application/pdf'),
            'class_id' => $classSectionId,
            'num_copies' => 1,
            'required_by' => '2026-09-13 11:00:00',
            'pickup_method' => 'Self Pickup',
            'page_selection' => 'one through five',
        ]);

        $response
            ->assertUnprocessable()
            ->assertJsonPath('status', false)
            ->assertJsonStructure(['errors' => ['page_selection']]);
    }

    public function test_teacher_can_upload_and_download_own_file_and_admin_can_download_any_request_file(): void
    {
        $teacher = $this->createUserWithRole('teacher');
        $admin = $this->createUserWithRole('administrator');
        $classSectionId = $this->seedClassSection();

        $this->actingAs($teacher, 'api');
        $createResponse = $this->post('/api/v1/print-requests', [
            'file' => UploadedFile::fake()->create('lesson.pdf', 12, 'application/pdf'),
            'class_id' => $classSectionId,
            'num_copies' => 2,
            'required_by' => '2026-09-13 11:00:00',
            'pickup_method' => 'Self Pickup',
            'page_selection' => '1-3,5',
        ]);

        $createResponse
            ->assertCreated()
            ->assertJsonPath('status', true);

        $requestId = (int) $createResponse->json('data.print_request.id');

        $this->actingAs($teacher, 'api');
        $this->get("/api/v1/print-requests/{$requestId}/file")
            ->assertOk();

        $this->actingAs($admin, 'api');
        $this->get("/api/v1/print-requests/{$requestId}/file")
            ->assertOk();
    }

    private function createUserWithRole(string $roleName): User
    {
        $role = Role::query()->firstOrCreate(
            ['name' => $roleName],
            [
                'slug' => $roleName,
                'description' => ucfirst(str_replace('_', ' ', $roleName)),
                'dashboard_route' => $roleName === 'teacher' ? 'teacher/classes' : 'administrator/administratordashboard',
                'priority' => 1,
                'is_active' => 1,
            ]
        );

        $user = User::factory()->create([
            'status' => 'Active',
            'is_verified' => 1,
            'is_suspended' => 0,
            'email' => $roleName . '-print-request-' . str_replace('.', '', uniqid('', true)) . '@example.test',
        ]);

        $user->roles()->syncWithoutDetaching([$role->id]);

        return $user->fresh() ?? $user;
    }

    private function seedClassSection(): int
    {
        DB::table('classSection')->insert([
            'class_id' => 1,
            'class_section_id' => 9901,
            'class_section_name' => 'Print Request Test Class',
            'semester' => 'Fall',
            'school_year' => '2026-2027',
        ]);

        return 9901;
    }

    /**
     * @param array<string, mixed> $overrides
     */
    private function seedPrintRequest(User $teacher, array $overrides = []): PrintRequest
    {
        return PrintRequest::query()->create(array_merge([
            'teacher_id' => $teacher->id,
            'admin_id' => null,
            'class_id' => $this->seedClassSection(),
            'file_path' => '',
            'page_selection' => null,
            'num_copies' => 1,
            'required_by' => '2026-09-13 11:00:00',
            'pickup_method' => 'Self Pickup',
            'status' => 'not_assigned',
        ], $overrides));
    }
}
