<?php

namespace Tests\Unit\SystemCore;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\SystemCore\App\Models\CoreCustomer;
use Modules\SystemCore\App\Models\CoreProduct;
use Modules\SystemCore\App\Models\CorePurchaseOrder;
use Modules\SystemCore\App\Models\CoreSalesOrder;
use Modules\SystemCore\App\Models\CoreSupplier;
use Tests\TestCase;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

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

    // ==========================================
    // Customer Code Tests
    // ==========================================

    public function test_customer_code_is_auto_generated_on_create(): 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_code_follows_pattern_cus_t_000001(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

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

        $this->assertMatchesRegularExpression('/^CUST-\d{6}$/', $customer->customer_code);
    }

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

        $customer1 = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $customer2 = $this->createCoreCustomer(['created_by_id' => $admin->id]);
        $customer3 = $this->createCoreCustomer(['created_by_id' => $admin->id]);

        // Codes should be sequential
        $code1Num = (int) str_replace('CUST-', '', $customer1->customer_code);
        $code2Num = (int) str_replace('CUST-', '', $customer2->customer_code);
        $code3Num = (int) str_replace('CUST-', '', $customer3->customer_code);

        $this->assertGreaterThan($code1Num, $code2Num);
        $this->assertGreaterThan($code2Num, $code3Num);
    }

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

        $customCode = 'CUSTOM-CODE-123';
        $customer = $this->createCoreCustomer([
            'customer_code' => $customCode,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($customCode, $customer->customer_code);
    }

    public function test_generate_customer_code_static_method(): void
    {
        $code = CoreCustomer::generateCustomerCode();

        $this->assertNotNull($code);
        $this->assertStringStartsWith('CUST-', $code);
        $this->assertMatchesRegularExpression('/^CUST-\d{6}$/', $code);
    }

    // ==========================================
    // Supplier Code Tests
    // ==========================================

    public function test_supplier_code_is_auto_generated_on_create(): 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_follows_pattern_sup_p_000001(): void
    {
        $admin = $this->createAdmin();
        $this->actingAs($admin);

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

        $this->assertMatchesRegularExpression('/^SUPP-\d{6}$/', $supplier->supplier_code);
    }

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

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

        $code1Num = (int) str_replace('SUPP-', '', $supplier1->supplier_code);
        $code2Num = (int) str_replace('SUPP-', '', $supplier2->supplier_code);

        $this->assertGreaterThan($code1Num, $code2Num);
    }

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

        $customCode = 'VENDOR-123';
        $supplier = $this->createCoreSupplier([
            'supplier_code' => $customCode,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($customCode, $supplier->supplier_code);
    }

    public function test_generate_supplier_code_static_method(): void
    {
        $code = CoreSupplier::generateSupplierCode();

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

    // ==========================================
    // Product Code Tests
    // ==========================================

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

        $product = $this->createCoreProduct(['created_by_id' => $admin->id]);

        $this->assertNotNull($product->product_code);
        $this->assertStringStartsWith('PROD-', $product->product_code);
    }

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

        $product = $this->createCoreProduct(['created_by_id' => $admin->id]);

        $this->assertMatchesRegularExpression('/^PROD-\d{6}$/', $product->product_code);
    }

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

        $product1 = $this->createCoreProduct(['created_by_id' => $admin->id]);
        $product2 = $this->createCoreProduct(['created_by_id' => $admin->id]);

        $code1Num = (int) str_replace('PROD-', '', $product1->product_code);
        $code2Num = (int) str_replace('PROD-', '', $product2->product_code);

        $this->assertGreaterThan($code1Num, $code2Num);
    }

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

        $customCode = 'ITEM-999';
        $product = $this->createCoreProduct([
            'product_code' => $customCode,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($customCode, $product->product_code);
    }

    public function test_generate_product_code_static_method(): void
    {
        $code = CoreProduct::generateProductCode();

        $this->assertNotNull($code);
        $this->assertStringStartsWith('PROD-', $code);
    }

    // ==========================================
    // Sales Order Number Tests
    // ==========================================

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

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);

        $this->assertNotNull($order->order_number);
        $this->assertStringStartsWith('SO-', $order->order_number);
    }

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

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);
        $today = date('Ymd');

        $this->assertStringContains($today, $order->order_number);
    }

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

        $order = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);

        // Format: SO-YYYYMMDD-00001
        $this->assertMatchesRegularExpression('/^SO-\d{8}-\d{5}$/', $order->order_number);
    }

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

        $order1 = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);
        $order2 = $this->createCoreSalesOrder(['created_by_id' => $admin->id]);

        // Extract the sequence number
        preg_match('/SO-\d{8}-(\d{5})/', $order1->order_number, $matches1);
        preg_match('/SO-\d{8}-(\d{5})/', $order2->order_number, $matches2);

        $seq1 = (int) $matches1[1];
        $seq2 = (int) $matches2[1];

        $this->assertGreaterThan($seq1, $seq2);
    }

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

        $customNumber = 'CUSTOM-SO-123';
        $order = $this->createCoreSalesOrder([
            'order_number' => $customNumber,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($customNumber, $order->order_number);
    }

    public function test_generate_sales_order_number_static_method(): void
    {
        $orderNumber = CoreSalesOrder::generateOrderNumber();

        $this->assertNotNull($orderNumber);
        $this->assertStringStartsWith('SO-', $orderNumber);
    }

    // ==========================================
    // Purchase Order Number Tests
    // ==========================================

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

        $order = $this->createCorePurchaseOrder(['created_by_id' => $admin->id]);

        $this->assertNotNull($order->order_number);
        $this->assertStringStartsWith('PO-', $order->order_number);
    }

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

        $order = $this->createCorePurchaseOrder(['created_by_id' => $admin->id]);
        $today = date('Ymd');

        $this->assertStringContains($today, $order->order_number);
    }

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

        $order = $this->createCorePurchaseOrder(['created_by_id' => $admin->id]);

        // Format: PO-YYYYMMDD-00001
        $this->assertMatchesRegularExpression('/^PO-\d{8}-\d{5}$/', $order->order_number);
    }

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

        $order1 = $this->createCorePurchaseOrder(['created_by_id' => $admin->id]);
        $order2 = $this->createCorePurchaseOrder(['created_by_id' => $admin->id]);

        // Extract the sequence number
        preg_match('/PO-\d{8}-(\d{5})/', $order1->order_number, $matches1);
        preg_match('/PO-\d{8}-(\d{5})/', $order2->order_number, $matches2);

        $seq1 = (int) $matches1[1];
        $seq2 = (int) $matches2[1];

        $this->assertGreaterThan($seq1, $seq2);
    }

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

        $customNumber = 'CUSTOM-PO-456';
        $order = $this->createCorePurchaseOrder([
            'order_number' => $customNumber,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($customNumber, $order->order_number);
    }

    public function test_generate_purchase_order_number_static_method(): void
    {
        $orderNumber = CorePurchaseOrder::generateOrderNumber();

        $this->assertNotNull($orderNumber);
        $this->assertStringStartsWith('PO-', $orderNumber);
    }

    // ==========================================
    // Edge Cases
    // ==========================================

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

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

        $codes = array_map(fn ($c) => $c->customer_code, $customers);
        $uniqueCodes = array_unique($codes);

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

    /**
     * Helper to assert string contains substring.
     */
    protected function assertStringContains(string $needle, string $haystack): void
    {
        $this->assertTrue(
            str_contains($haystack, $needle),
            "Failed asserting that '{$haystack}' contains '{$needle}'"
        );
    }
}
