<?php

namespace Tests\Unit\SystemCore\Models;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\SystemCore\App\Models\CoreCustomer;
use Modules\SystemCore\App\Models\CustomerDocument;
use Tests\TestCase;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

class CustomerDocumentTest extends TestCase
{
    use CreatesSystemCoreEntities;
    use CreatesUsers;
    use RefreshDatabase;

    // ==========================================
    // Creation Tests
    // ==========================================

    public function test_can_create_customer_document(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);

        $document = CustomerDocument::create([
            'customer_id' => $customer->id,
            'document_title' => 'Contract Agreement',
            'document_number' => 'DOC-001',
            'document_type' => 'contract',
            'file_path' => 'documents/contract.pdf',
            'file_name' => 'contract.pdf',
            'is_active' => true,
        ]);

        $this->assertDatabaseHas('customer_documents', [
            'customer_id' => $customer->id,
            'document_title' => 'Contract Agreement',
        ]);
        $this->assertNotNull($document->id);
    }

    // ==========================================
    // Relationship Tests
    // ==========================================

    public function test_document_belongs_to_customer(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument(['created_by_id' => $admin->id]);

        $this->assertNotNull($document->customer);
        $this->assertInstanceOf(CoreCustomer::class, $document->customer);
    }

    // ==========================================
    // Expiry Method Tests
    // ==========================================

    public function test_is_expired_returns_true_for_past_date(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createExpiredCustomerDocument(['created_by_id' => $admin->id]);

        $this->assertTrue($document->isExpired());
    }

    public function test_is_expired_returns_false_for_future_date(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'expiry_date' => now()->addYear(),
            'created_by_id' => $admin->id,
        ]);

        $this->assertFalse($document->isExpired());
    }

    public function test_is_expired_returns_false_when_no_expiry_date(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'expiry_date' => null,
            'created_by_id' => $admin->id,
        ]);

        $this->assertFalse($document->isExpired());
    }

    public function test_is_about_to_expire_within_30_days(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createAboutToExpireCustomerDocument(15, ['created_by_id' => $admin->id]);

        $this->assertTrue($document->isAboutToExpire(30));
    }

    public function test_is_about_to_expire_with_custom_days(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $customer = $this->createCoreCustomer(['created_by_id' => $admin->id]);

        // Test with a document expiring in exactly 10 days from today (at midnight)
        $expiryDate = now()->startOfDay()->addDays(10);

        $document = CustomerDocument::create([
            'customer_id' => $customer->id,
            'document_title' => 'Test Doc 10 days',
            'document_number' => 'DOC-10DAYS',
            'document_type' => 'contract',
            'file_path' => 'documents/test.pdf',
            'file_name' => 'test.pdf',
            'expiry_date' => $expiryDate,
            'is_active' => true,
        ]);

        // 10 days away should be "about to expire" within 15 days
        $this->assertTrue($document->isAboutToExpire(15));
        // 10 days away should be "about to expire" within 10 days (edge case)
        $this->assertTrue($document->isAboutToExpire(10));
        // 10 days away should NOT be "about to expire" within 8 days
        $this->assertFalse($document->isAboutToExpire(8));
    }

    public function test_is_about_to_expire_returns_false_when_expired(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createExpiredCustomerDocument(['created_by_id' => $admin->id]);

        $this->assertFalse($document->isAboutToExpire());
    }

    public function test_is_about_to_expire_returns_false_when_no_expiry_date(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'expiry_date' => null,
            'created_by_id' => $admin->id,
        ]);

        $this->assertFalse($document->isAboutToExpire());
    }

    // ==========================================
    // Scope Tests
    // ==========================================

    public function test_expired_scope_filters_correctly(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $expiredDoc = $this->createExpiredCustomerDocument(['created_by_id' => $admin->id]);
        $activeDoc = $this->createCustomerDocument([
            'expiry_date' => now()->addYear(),
            'created_by_id' => $admin->id,
        ]);

        $expiredDocs = CustomerDocument::expired()->get();

        $this->assertTrue($expiredDocs->contains($expiredDoc));
        $this->assertFalse($expiredDocs->contains($activeDoc));
    }

    public function test_about_to_expire_scope_with_default_days(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $aboutToExpireDoc = $this->createAboutToExpireCustomerDocument(15, ['created_by_id' => $admin->id]);
        $notExpiringSoon = $this->createCustomerDocument([
            'expiry_date' => now()->addMonths(3),
            'created_by_id' => $admin->id,
        ]);
        $expiredDoc = $this->createExpiredCustomerDocument(['created_by_id' => $admin->id]);

        $aboutToExpireDocs = CustomerDocument::aboutToExpire(30)->get();

        $this->assertTrue($aboutToExpireDocs->contains($aboutToExpireDoc));
        $this->assertFalse($aboutToExpireDocs->contains($notExpiringSoon));
        $this->assertFalse($aboutToExpireDocs->contains($expiredDoc));
    }

    public function test_about_to_expire_scope_with_custom_days(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $doc15Days = $this->createCustomerDocument([
            'expiry_date' => now()->addDays(15),
            'created_by_id' => $admin->id,
        ]);

        $doc45Days = $this->createCustomerDocument([
            'expiry_date' => now()->addDays(45),
            'created_by_id' => $admin->id,
        ]);

        $scopeResult = CustomerDocument::aboutToExpire(20)->get();

        $this->assertTrue($scopeResult->contains($doc15Days));
        $this->assertFalse($scopeResult->contains($doc45Days));
    }

    public function test_active_scope_filters_correctly(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $activeDoc = $this->createCustomerDocument([
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);
        $inactiveDoc = $this->createCustomerDocument([
            'is_active' => false,
            'created_by_id' => $admin->id,
        ]);

        $activeDocs = CustomerDocument::active()->get();

        $this->assertTrue($activeDocs->contains($activeDoc));
        $this->assertFalse($activeDocs->contains($inactiveDoc));
    }

    public function test_by_type_scope_filters_correctly(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $contractDoc = $this->createCustomerDocument([
            'document_type' => 'contract',
            'created_by_id' => $admin->id,
        ]);
        $licenseDoc = $this->createCustomerDocument([
            'document_type' => 'license',
            'created_by_id' => $admin->id,
        ]);

        $contracts = CustomerDocument::byType('contract')->get();

        $this->assertTrue($contracts->contains($contractDoc));
        $this->assertFalse($contracts->contains($licenseDoc));
    }

    // ==========================================
    // Accessor Tests
    // ==========================================

    public function test_file_url_attribute_returns_storage_url(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'file_path' => 'documents/test-file.pdf',
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($document->file_url);
        $this->assertStringContainsString('documents/test-file.pdf', $document->file_url);
    }

    public function test_file_url_returns_null_when_no_file(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        // Use empty string since file_path column doesn't allow null
        // The accessor checks if (! $this->file_path) which handles empty strings
        $document = $this->createCustomerDocument([
            'file_path' => '',
            'created_by_id' => $admin->id,
        ]);

        $this->assertNull($document->file_url);
    }

    public function test_formatted_file_size_attribute(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'file_size' => 2048,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('2 KB', $document->formatted_file_size);
    }

    public function test_formatted_file_size_handles_bytes(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'file_size' => 500,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('500 B', $document->formatted_file_size);
    }

    public function test_formatted_file_size_handles_mb(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'file_size' => 2097152, // 2 MB
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('2 MB', $document->formatted_file_size);
    }

    public function test_formatted_file_size_returns_na_when_null(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'file_size' => null,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('N/A', $document->formatted_file_size);
    }

    // ==========================================
    // Casting Tests
    // ==========================================

    public function test_date_fields_cast_correctly(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $issueDate = now()->subMonth();
        $expiryDate = now()->addYear();

        $document = $this->createCustomerDocument([
            'issue_date' => $issueDate,
            'expiry_date' => $expiryDate,
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(\Carbon\Carbon::class, $document->issue_date);
        $this->assertInstanceOf(\Carbon\Carbon::class, $document->expiry_date);
    }

    public function test_is_active_casts_to_boolean(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($document->is_active);
        $this->assertIsBool($document->is_active);
    }

    public function test_file_size_casts_to_integer(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument([
            'file_size' => 1024,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(1024, $document->file_size);
        $this->assertIsInt($document->file_size);
    }

    // ==========================================
    // Soft Delete Tests
    // ==========================================

    public function test_document_can_be_soft_deleted(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $document = $this->createCustomerDocument(['created_by_id' => $admin->id]);
        $documentId = $document->id;

        $document->delete();

        $this->assertSoftDeleted('customer_documents', ['id' => $documentId]);
    }
}
