<?php

namespace Tests\Feature\PublicSite;

use App\Domain\Documents\Models\Document;
use App\Domain\Documents\Models\DocumentType;
use App\Domain\Identity\Models\User;
use App\Domain\Organization\Models\Branch;
use App\Domain\Organization\Models\Company;
use App\Domain\Website\Enums\RequisitionStatus;
use App\Domain\Website\Models\EmployerRequisition;
use Database\Seeders\DocumentTypeSeeder;
use Database\Seeders\PermissionSeeder;
use Database\Seeders\RoleSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Http\UploadedFile;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Storage;
use Tests\TestCase;

class RequisitionDocumentTest extends TestCase
{
    use RefreshDatabase;

    private Company $company;

    private string $reference;

    protected function setUp(): void
    {
        parent::setUp();
        Storage::fake('local');
        Cache::flush();
        $this->seed([PermissionSeeder::class, RoleSeeder::class]);

        $this->company = Company::factory()->create();
        Branch::factory()->for($this->company)->create(['is_head_office' => true]);
        $this->seed(DocumentTypeSeeder::class);

        $this->reference = $this->postJson('/api/v1/site/requisitions', [
            'company_name' => 'Al Rajhi Contracting',
            'country' => 'SA',
            'industry' => 'Construction',
            'company_address' => 'Riyadh',
            'contact_name' => 'Faisal',
            'contact_designation' => 'HR',
            'contact_email' => 'faisal@example.test',
            'contact_phone' => '+966500000000',
            'consent' => true,
            'positions' => [[
                'job_title' => 'Driver',
                'quantity' => 5,
                'salary_amount' => 180000,
                'salary_currency' => 'SAR',
            ]],
        ])->assertCreated()->json('data.reference');
    }

    private function upload(array $payload = [], ?UploadedFile $file = null, ?string $reference = null)
    {
        return $this->post(
            '/api/v1/site/requisitions/'.($reference ?? $this->reference).'/documents',
            array_merge([
                'document_type' => 'demand_letter',
                'file' => $file ?? UploadedFile::fake()->create('demand.pdf', 200, 'application/pdf'),
            ], $payload),
            ['Accept' => 'application/json'],
        );
    }

    public function test_an_employer_can_attach_a_supporting_document(): void
    {
        $this->upload()->assertCreated()->assertJsonCount(1, 'data');

        $document = Document::firstOrFail();
        $requisition = EmployerRequisition::firstOrFail();

        $this->assertSame($requisition->getMorphClass(), $document->owner_type);
        $this->assertSame($requisition->id, $document->owner_id);
        // No authenticated actor exists on a public upload.
        $this->assertNull($document->uploaded_by);
        Storage::disk('local')->assertExists($document->file_path);
    }

    public function test_only_employer_document_types_are_accepted(): void
    {
        // A passport is an applicant document; not an employer's to attach here.
        $this->upload(['document_type' => 'passport'])
            ->assertStatus(422)
            ->assertJsonStructure(['errors' => ['document_type']]);

        $this->assertSame(0, Document::count());
    }

    public function test_the_token_is_required(): void
    {
        $this->upload(reference: 'not-a-real-token')->assertStatus(404);

        $this->assertSame(0, Document::count());
    }

    public function test_an_executable_is_refused(): void
    {
        $this->upload([], UploadedFile::fake()->create('payload.php', 10, 'application/pdf'))
            ->assertStatus(422);

        $this->assertSame(0, Document::count());
    }

    public function test_public_uploads_have_a_tighter_size_limit_than_staff_uploads(): void
    {
        // 8 MB is fine for staff but over the 5 MB public ceiling.
        $this->upload([], UploadedFile::fake()->create('big.pdf', 8192, 'application/pdf'))
            ->assertStatus(422)
            ->assertJsonStructure(['errors' => ['file']]);
    }

    public function test_the_number_of_attachments_is_capped(): void
    {
        config(['mams.requisitions.max_documents' => 2]);

        $this->upload()->assertCreated();
        $this->upload(['document_type' => 'power_of_attorney'])->assertCreated();
        $this->upload(['document_type' => 'employer_trade_license'])->assertStatus(422);

        $this->assertSame(2, Document::count());
    }

    public function test_a_closed_requisition_accepts_no_more_attachments(): void
    {
        EmployerRequisition::firstOrFail()->update([
            'status' => RequisitionStatus::Rejected->value,
        ]);

        $this->upload()->assertStatus(404);
        $this->getJson("/api/v1/site/requisitions/{$this->reference}/documents")->assertStatus(404);
    }

    public function test_the_employer_sees_what_they_sent_but_not_how_it_was_reviewed(): void
    {
        $this->upload()->assertCreated();

        Document::firstOrFail()->update([
            'verification_status' => 'rejected',
            'rejection_reason' => 'Not signed.',
            'internal_notes' => 'INTERNAL: verify against the ministry register.',
        ]);

        $content = $this->getJson("/api/v1/site/requisitions/{$this->reference}/documents")
            ->assertOk()
            ->assertJsonCount(1, 'data')
            ->getContent();

        $this->assertStringNotContainsString('INTERNAL', $content);
        $this->assertStringNotContainsString('rejection_reason', $content);
        $this->assertStringNotContainsString('verification_status', $content);
    }

    public function test_staff_see_the_attachments_on_the_requisition(): void
    {
        $this->upload()->assertCreated();

        $manager = tap(User::factory()->create([
            'company_id' => $this->company->id,
            'branch_id' => Branch::first()->id,
        ]))->assignRole('recruitment-manager');

        $requisition = EmployerRequisition::firstOrFail();

        $this->actingAs($manager)
            ->getJson("/api/v1/employer-requisitions/{$requisition->uuid}")
            ->assertOk()
            ->assertJsonCount(1, 'data.documents')
            ->assertJsonPath('data.documents.0.document_type.code', 'demand_letter');
    }

    public function test_a_reader_without_document_permission_sees_no_attachments(): void
    {
        $this->upload()->assertCreated();

        // Payroll officers can see nothing here at all, so use a role that can
        // read requisitions but holds no document permission.
        $role = \App\Domain\Identity\Models\Role::create([
            'name' => 'requisition-only',
            'guard_name' => 'web',
            'label' => 'Requisition only',
            'is_active' => true,
        ]);
        $permission = \Spatie\Permission\Models\Permission::where('name', 'requisition.view')->firstOrFail();
        $role->syncPermissions([$permission->name]);
        \Illuminate\Support\Facades\DB::table('role_permission_scopes')->insert([
            'role_id' => $role->id,
            'permission_id' => $permission->id,
            'scope' => 'company',
            'created_at' => now(), 'updated_at' => now(),
        ]);

        $user = tap(User::factory()->create(['company_id' => $this->company->id]))->assignRole($role);
        $requisition = EmployerRequisition::firstOrFail();

        $response = $this->actingAs($user)
            ->getJson("/api/v1/employer-requisitions/{$requisition->uuid}")
            ->assertOk();

        // The key is absent entirely, not an empty list.
        $this->assertArrayNotHasKey('documents', $response->json('data'));
    }

    public function test_the_seeded_catalogue_covers_every_allowed_type(): void
    {
        foreach (config('mams.requisitions.allowed_document_codes') as $code) {
            $this->assertTrue(
                DocumentType::where('company_id', $this->company->id)->where('code', $code)->exists(),
                "The document type [{$code}] is allowed for requisitions but is not seeded.",
            );
        }
    }
}
