<?php

namespace Tests\Unit\SystemCore\Models;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\SystemCore\App\Enums\SupplierStatus;
use Modules\SystemCore\App\Models\CoreAddress;
use Modules\SystemCore\App\Models\CoreContact;
use Modules\SystemCore\App\Models\CorePaymentTerm;
use Modules\SystemCore\App\Models\CorePurchaseOrder;
use Modules\SystemCore\App\Models\CoreSupplier;
use Modules\SystemCore\App\Models\SupplierDocument;
use Tests\TestCase;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

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

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

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

        $supplier = CoreSupplier::create([
            'name' => 'Test Supplier',
            'email' => 'supplier@example.com',
            'status' => SupplierStatus::Active,
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);

        $this->assertDatabaseHas('core_suppliers', [
            'name' => 'Test Supplier',
            'email' => 'supplier@example.com',
        ]);
        $this->assertNotNull($supplier->id);
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->assertNotNull($supplier->supplier_code);
        $this->assertStringStartsWith('SUPP-', $supplier->supplier_code);
    }

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

        $supplier = $this->createCoreSupplier([
            'supplier_code' => 'CUSTOM-001',
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('CUSTOM-001', $supplier->supplier_code);
    }

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

        $code1 = CoreSupplier::generateSupplierCode();
        $this->assertStringStartsWith('SUPP-', $code1);
        $this->assertMatchesRegularExpression('/^SUPP-\d{6}$/', $code1);
    }

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

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

        $supplier = $this->createCoreSupplier([
            'status' => SupplierStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(SupplierStatus::class, $supplier->status);
        $this->assertEquals(SupplierStatus::Active, $supplier->status);
    }

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

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

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

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

        $supplier = $this->createCoreSupplier([
            'credit_limit' => 50000.50,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(50000.50, $supplier->credit_limit);
    }

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

        $tags = ['electronics', 'wholesale'];
        $metadata = ['region' => 'APAC', 'tier' => 'gold'];

        $supplier = $this->createCoreSupplier([
            'tags' => $tags,
            'metadata' => $metadata,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($tags, $supplier->tags);
        $this->assertEquals($metadata, $supplier->metadata);
        $this->assertIsArray($supplier->tags);
        $this->assertIsArray($supplier->metadata);
    }

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

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

        $activeSupplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        $inactiveSupplier = $this->createInactiveSupplier(['created_by_id' => $admin->id]);

        $activeSuppliers = CoreSupplier::active()->get();

        $this->assertTrue($activeSuppliers->contains($activeSupplier));
        $this->assertFalse($activeSuppliers->contains($inactiveSupplier));
    }

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

        // is_active true but status not Active
        $supplier1 = $this->createCoreSupplier([
            'is_active' => true,
            'status' => SupplierStatus::Pending,
            'created_by_id' => $admin->id,
        ]);

        // is_active false but status Active
        $supplier2 = $this->createCoreSupplier([
            'is_active' => false,
            'status' => SupplierStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        // Both active
        $supplier3 = $this->createCoreSupplier([
            'is_active' => true,
            'status' => SupplierStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        $activeSuppliers = CoreSupplier::active()->get();

        $this->assertFalse($activeSuppliers->contains($supplier1));
        $this->assertFalse($activeSuppliers->contains($supplier2));
        $this->assertTrue($activeSuppliers->contains($supplier3));
    }

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

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

        $supplier = $this->createCoreSupplier([
            'address' => '123 Main Street',
            'city' => 'New York',
            'state' => 'NY',
            'postal_code' => '10001',
            'country' => 'USA',
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(
            '123 Main Street, New York, NY, 10001, USA',
            $supplier->full_address
        );
    }

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

        $supplier = $this->createCoreSupplier([
            'address' => null,
            'city' => null,
            'state' => null,
            'postal_code' => null,
            'country' => null,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNull($supplier->full_address);
    }

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

        $supplier = $this->createCoreSupplier([
            'address' => '123 Main Street',
            'city' => 'New York',
            'state' => null,
            'postal_code' => '',
            'country' => 'USA',
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(
            '123 Main Street, New York, USA',
            $supplier->full_address
        );
    }

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

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createCoreAddress([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'address_type' => 'billing',
        ]);

        $this->createCoreAddress([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'address_type' => 'shipping',
            'is_primary' => false,
        ]);

        $supplier->refresh();

        $this->assertCount(2, $supplier->addresses);
        $this->assertInstanceOf(CoreAddress::class, $supplier->addresses->first());
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createCoreAddress([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'is_primary' => true,
            'address_line_1' => 'Primary Address',
        ]);

        $this->createCoreAddress([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'is_primary' => false,
            'address_line_1' => 'Secondary Address',
        ]);

        $supplier->refresh();

        $this->assertNotNull($supplier->primaryAddress);
        $this->assertEquals('Primary Address', $supplier->primaryAddress->address_line_1);
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createCoreContact([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'first_name' => 'John',
        ]);

        $this->createCoreContact([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'first_name' => 'Jane',
            'is_primary' => false,
        ]);

        $supplier->refresh();

        $this->assertCount(2, $supplier->contacts);
        $this->assertInstanceOf(CoreContact::class, $supplier->contacts->first());
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createCoreContact([
            'entity_type' => 'supplier',
            'entity_id' => $supplier->id,
            'first_name' => 'Primary',
            'last_name' => 'Contact',
            'is_primary' => true,
        ]);

        $supplier->refresh();

        $this->assertNotNull($supplier->primaryContact);
        $this->assertEquals('Primary', $supplier->primaryContact->first_name);
    }

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

        $paymentTerm = $this->createCorePaymentTerm(['created_by_id' => $admin->id]);

        $supplier = $this->createCoreSupplier([
            'payment_term_id' => $paymentTerm->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($supplier->paymentTerm);
        $this->assertInstanceOf(CorePaymentTerm::class, $supplier->paymentTerm);
        $this->assertEquals($paymentTerm->id, $supplier->paymentTerm->id);
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createCorePurchaseOrder([
            'supplier_id' => $supplier->id,
            'created_by_id' => $admin->id,
        ]);

        $this->createCorePurchaseOrder([
            'supplier_id' => $supplier->id,
            'created_by_id' => $admin->id,
        ]);

        $supplier->refresh();

        $this->assertCount(2, $supplier->purchaseOrders);
        $this->assertInstanceOf(CorePurchaseOrder::class, $supplier->purchaseOrders->first());
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createCorePurchaseOrder([
            'supplier_id' => $supplier->id,
            'created_by_id' => $admin->id,
        ]);

        $supplier->refresh();

        $this->assertCount(1, $supplier->purchases);
        $this->assertEquals(
            $supplier->purchaseOrders->pluck('id')->toArray(),
            $supplier->purchases->pluck('id')->toArray()
        );
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);

        $this->createSupplierDocument([
            'supplier_id' => $supplier->id,
            'created_by_id' => $admin->id,
        ]);

        $supplier->refresh();

        $this->assertCount(1, $supplier->documents);
        $this->assertInstanceOf(SupplierDocument::class, $supplier->documents->first());
    }

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

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        $supplierId = $supplier->id;

        $supplier->delete();

        $this->assertSoftDeleted('core_suppliers', ['id' => $supplierId]);
    }

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

        $supplier = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        $supplierId = $supplier->id;

        $supplier->delete();
        $this->assertSoftDeleted('core_suppliers', ['id' => $supplierId]);

        $supplier->restore();
        $this->assertDatabaseHas('core_suppliers', [
            'id' => $supplierId,
            'deleted_at' => null,
        ]);
    }

    // ==========================================
    // Unique Code Tests
    // ==========================================

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

        $suppliers = [];
        for ($i = 0; $i < 5; $i++) {
            $suppliers[] = $this->createCoreSupplier(['created_by_id' => $admin->id]);
        }

        $codes = array_map(fn ($s) => $s->supplier_code, $suppliers);
        $uniqueCodes = array_unique($codes);

        $this->assertCount(5, $uniqueCodes, 'All supplier codes should be unique');
    }
}
