<?php

namespace Tests\Feature\SystemCore\Models;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\SystemCore\App\Enums\CustomerStatus;
use Modules\SystemCore\App\Enums\CustomerType;
use Modules\SystemCore\App\Models\CoreAddress;
use Modules\SystemCore\App\Models\CoreContact;
use Modules\SystemCore\App\Models\CoreCustomer;
use Tests\TestCase;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

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

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

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

        $customer = CoreCustomer::create([
            'name' => 'Test Customer',
            'email' => 'test@example.com',
            'customer_type' => CustomerType::B2B,
            'status' => CustomerStatus::Active,
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);

        $this->assertDatabaseHas('core_customers', [
            'name' => 'Test Customer',
            'email' => 'test@example.com',
        ]);
    }

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

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

        $this->assertNotNull($customer->customer_code);
        $this->assertStringStartsWith('CUST-', $customer->customer_code);
    }

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

        $customer = $this->createCoreCustomer([
            'customer_type' => CustomerType::B2B,
            'status' => CustomerStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(CustomerType::class, $customer->customer_type);
        $this->assertInstanceOf(CustomerStatus::class, $customer->status);
        $this->assertEquals(CustomerType::B2B, $customer->customer_type);
        $this->assertEquals(CustomerStatus::Active, $customer->status);
    }

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

        $customer = $this->createCoreCustomer([
            'is_active' => true,
            'is_blacklisted' => false,
            'tax_exempt' => true,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($customer->is_active);
        $this->assertFalse($customer->is_blacklisted);
        $this->assertTrue($customer->tax_exempt);
    }

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

        $customer = $this->createCoreCustomer([
            'first_purchase_date' => now()->subMonth(),
            'last_purchase_date' => now()->subWeek(),
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(\Carbon\Carbon::class, $customer->first_purchase_date);
        $this->assertInstanceOf(\Carbon\Carbon::class, $customer->last_purchase_date);
    }

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

        $tags = ['vip', 'priority', 'retail'];
        $metadata = ['source' => 'website', 'referral' => 'google'];

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

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

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

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

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

        // Create addresses for this customer
        CoreAddress::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'address_type' => 'billing',
            'address_line_1' => '123 Main St',
            'city' => 'Test City',
            'country' => 'US',
            'is_primary' => true,
            'created_by_id' => $admin->id,
        ]);

        CoreAddress::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'address_type' => 'shipping',
            'address_line_1' => '456 Ship St',
            'city' => 'Ship City',
            'country' => 'US',
            'is_primary' => false,
            'created_by_id' => $admin->id,
        ]);

        $this->assertCount(2, $customer->addresses);
    }

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

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

        CoreAddress::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'address_type' => 'billing',
            'address_line_1' => '123 Main St',
            'city' => 'Test City',
            'country' => 'US',
            'is_primary' => true,
            'created_by_id' => $admin->id,
        ]);

        CoreAddress::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'address_type' => 'shipping',
            'address_line_1' => '456 Ship St',
            'city' => 'Ship City',
            'country' => 'US',
            'is_primary' => false,
            'created_by_id' => $admin->id,
        ]);

        $this->assertNotNull($customer->primaryAddress);
        $this->assertEquals('123 Main St', $customer->primaryAddress->address_line_1);
    }

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

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

        CoreContact::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'contact_type' => 'primary',
            'first_name' => 'John',
            'last_name' => 'Doe',
            'email' => 'john@example.com',
            'is_primary' => true,
        ]);

        CoreContact::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'contact_type' => 'billing',
            'first_name' => 'Jane',
            'last_name' => 'Doe',
            'email' => 'jane@example.com',
            'is_primary' => false,
        ]);

        $this->assertCount(2, $customer->contacts);
    }

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

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

        CoreContact::create([
            'entity_type' => 'customer',
            'entity_id' => $customer->id,
            'contact_type' => 'primary',
            'first_name' => 'Primary',
            'last_name' => 'Contact',
            'email' => 'primary@example.com',
            'is_primary' => true,
        ]);

        $this->assertNotNull($customer->primaryContact);
        // CoreContact uses full_name accessor for combined first_name + last_name
        $this->assertEquals('Primary Contact', $customer->primaryContact->full_name);
    }

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

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

        $this->createCoreSalesOrder([
            'customer_id' => $customer->id,
            'created_by_id' => $admin->id,
        ]);

        $this->createCoreSalesOrder([
            'customer_id' => $customer->id,
            'created_by_id' => $admin->id,
        ]);

        $this->assertCount(2, $customer->salesOrders);
    }

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

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

        $this->assertNotNull($customer->paymentTerm);
        $this->assertEquals($paymentTerm->id, $customer->paymentTerm->id);
    }

    // ==========================================
    // Display Attribute Tests
    // ==========================================

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

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

        $this->assertEquals('Acme Corporation', $customer->display_name);
    }

    public function test_display_name_returns_name_when_set(): void
    {
        // Note: name is required in the database, so we can't test null case
        // This test verifies the display_name accessor works correctly
        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $customer = $this->createCoreCustomer([
            'name' => 'Test Customer Name',
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('Test Customer Name', $customer->display_name);
    }

    // ==========================================
    // Classification Tests
    // ==========================================

    public function test_get_classification_uses_customer_group_if_set(): void
    {
        if (! moduleExists('CRMCore')) {
            $this->markTestSkipped('CRMCore module is not enabled');
        }

        $admin = $this->createAdmin();
        $this->actingAs($admin);

        $customerGroup = \Modules\CRMCore\App\Models\CustomerGroup::create([
            'name' => 'Premium Clients',
            'code' => 'PREMIUM',
            'is_active' => true,
            'created_by_id' => $admin->id,
        ]);

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

        $this->assertEquals('Premium Clients', $customer->getClassification());
    }

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

        $customer = $this->createCoreCustomer([
            'customer_group_id' => null,
            'company_id' => null, // B2C
            'lifetime_value' => 5000, // basic segment
            'created_by_id' => $admin->id,
        ]);

        $classification = $customer->getClassification();

        $this->assertStringContainsString('B2C', $classification);
        $this->assertStringContainsString('basic', $classification);
    }

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

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

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

        $customer->delete();

        $this->assertSoftDeleted('core_customers', ['id' => $customerId]);
    }

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

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

        $customer->delete();
        $customer->restore();

        $this->assertDatabaseHas('core_customers', [
            'id' => $customer->id,
            'deleted_at' => null,
        ]);
    }

    // ==========================================
    // Tax Exempt Tests
    // ==========================================

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

        $taxExempt = $this->createCoreCustomer([
            'tax_exempt' => true,
            'created_by_id' => $admin->id,
        ]);

        $notExempt = $this->createCoreCustomer([
            'tax_exempt' => false,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($taxExempt->isTaxExempt());
        $this->assertFalse($notExempt->isTaxExempt());
    }

    // ==========================================
    // Has Purchased Tests
    // ==========================================

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

        $purchased = $this->createCoreCustomer([
            'purchase_count' => 5,
            'created_by_id' => $admin->id,
        ]);

        $notPurchased = $this->createCoreCustomer([
            'purchase_count' => 0,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($purchased->hasPurchased());
        $this->assertFalse($notPurchased->hasPurchased());
    }

    // ==========================================
    // Create From First Purchase Tests
    // ==========================================

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

        $customer = CoreCustomer::createFromFirstPurchase([
            'name' => 'New Walk-In Customer',
            'email' => 'walkin@example.com',
            'customer_type' => CustomerType::B2C,
            'status' => CustomerStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        $this->assertDatabaseHas('core_customers', [
            'name' => 'New Walk-In Customer',
            'email' => 'walkin@example.com',
        ]);

        // Check defaults
        $this->assertTrue($customer->is_active);
        $this->assertFalse($customer->is_blacklisted);
        $this->assertEquals(0, $customer->lifetime_value);
        $this->assertEquals(0, $customer->purchase_count);
    }

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

        $customer = CoreCustomer::createFromFirstPurchase(
            [
                'name' => 'New Customer',
                'email' => 'new@example.com',
                'customer_type' => CustomerType::B2C,
                'status' => CustomerStatus::Active,
                'created_by_id' => $admin->id,
            ],
            ['amount' => 500.00]
        );

        $this->assertEquals(1, $customer->purchase_count);
        $this->assertEquals(500.00, $customer->lifetime_value);
    }

    // ==========================================
    // Billing Entity Tests
    // ==========================================

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

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

        $billingEntity = $customer->getBillingEntity();

        $this->assertInstanceOf(CoreCustomer::class, $billingEntity);
        $this->assertEquals($customer->id, $billingEntity->id);
    }
}
