<?php

namespace Tests\Unit\SystemCore\Models;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Modules\SystemCore\App\Enums\ProductStatus;
use Modules\SystemCore\App\Models\CorePricingRule;
use Modules\SystemCore\App\Models\CoreProduct;
use Modules\SystemCore\App\Models\CoreProductCategory;
use Modules\SystemCore\App\Models\CoreProductVariant;
use Modules\SystemCore\App\Models\CoreTaxConfiguration;
use Tests\TestCase;
use Tests\Traits\CreatesSystemCoreEntities;
use Tests\Traits\CreatesUsers;

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

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

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

        $category = $this->createCoreProductCategory(['created_by_id' => $admin->id]);

        $product = CoreProduct::create([
            'name' => 'Test Product',
            'sku' => 'TEST-SKU-001',
            'category_id' => $category->id,
            'status' => ProductStatus::Active,
            'is_active' => true,
            'selling_price' => 99.99,
            'created_by_id' => $admin->id,
        ]);

        $this->assertDatabaseHas('core_products', [
            'name' => 'Test Product',
            'sku' => 'TEST-SKU-001',
        ]);
        $this->assertNotNull($product->id);
    }

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

        $product = $this->createCoreProduct([
            'product_code' => 'CUSTOM-PROD-001',
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals('CUSTOM-PROD-001', $product->product_code);
    }

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

        $code1 = CoreProduct::generateProductCode();
        $this->assertStringStartsWith('PROD-', $code1);
        $this->assertMatchesRegularExpression('/^PROD-\d{6}$/', $code1);
    }

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

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

        $product = $this->createCoreProduct([
            'status' => ProductStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        $this->assertInstanceOf(ProductStatus::class, $product->status);
        $this->assertEquals(ProductStatus::Active, $product->status);
    }

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

        $product = $this->createCoreProduct([
            'is_active' => true,
            'is_trackable' => true,
            'has_variants' => false,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($product->is_active);
        $this->assertTrue($product->is_trackable);
        $this->assertFalse($product->has_variants);
        $this->assertIsBool($product->is_active);
        $this->assertIsBool($product->is_trackable);
        $this->assertIsBool($product->has_variants);
    }

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

        $product = $this->createCoreProduct([
            'base_price' => 79.99,
            'cost_price' => 39.99,
            'selling_price' => 99.99,
            'weight' => 2.5,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(79.99, $product->base_price);
        $this->assertEquals(39.99, $product->cost_price);
        $this->assertEquals(99.99, $product->selling_price);
        $this->assertEquals(2.5, $product->weight);
    }

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

        $product = $this->createCoreProduct([
            'min_stock_level' => 10,
            'max_stock_level' => 1000,
            'reorder_level' => 50,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals(10, $product->min_stock_level);
        $this->assertEquals(1000, $product->max_stock_level);
        $this->assertEquals(50, $product->reorder_level);
        $this->assertIsInt($product->min_stock_level);
    }

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

        $dimensions = ['length' => 10, 'width' => 5, 'height' => 3];
        $images = ['image1.jpg', 'image2.jpg'];
        $tags = ['electronics', 'gadget'];
        $metadata = ['brand_origin' => 'Japan', 'warranty' => '2 years'];

        $product = $this->createCoreProduct([
            'dimensions' => $dimensions,
            'images' => $images,
            'tags' => $tags,
            'metadata' => $metadata,
            'created_by_id' => $admin->id,
        ]);

        $this->assertEquals($dimensions, $product->dimensions);
        $this->assertEquals($images, $product->images);
        $this->assertEquals($tags, $product->tags);
        $this->assertEquals($metadata, $product->metadata);
        $this->assertIsArray($product->dimensions);
        $this->assertIsArray($product->images);
    }

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

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

        $activeProduct = $this->createCoreProduct(['created_by_id' => $admin->id]);
        $inactiveProduct = $this->createInactiveProduct(['created_by_id' => $admin->id]);

        $activeProducts = CoreProduct::active()->get();

        $this->assertTrue($activeProducts->contains($activeProduct));
        $this->assertFalse($activeProducts->contains($inactiveProduct));
    }

    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
        $product1 = $this->createCoreProduct([
            'is_active' => true,
            'status' => ProductStatus::Draft,
            'created_by_id' => $admin->id,
        ]);

        // is_active false but status Active
        $product2 = $this->createCoreProduct([
            'is_active' => false,
            'status' => ProductStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        // Both active
        $product3 = $this->createCoreProduct([
            'is_active' => true,
            'status' => ProductStatus::Active,
            'created_by_id' => $admin->id,
        ]);

        $activeProducts = CoreProduct::active()->get();

        $this->assertFalse($activeProducts->contains($product1));
        $this->assertFalse($activeProducts->contains($product2));
        $this->assertTrue($activeProducts->contains($product3));
    }

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

        $category1 = $this->createCoreProductCategory(['created_by_id' => $admin->id]);
        $category2 = $this->createCoreProductCategory(['created_by_id' => $admin->id]);

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

        $category1Products = CoreProduct::byCategory($category1->id)->get();

        $this->assertTrue($category1Products->contains($product1));
        $this->assertFalse($category1Products->contains($product2));
    }

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

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

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

        $this->assertNotNull($product->category);
        $this->assertInstanceOf(CoreProductCategory::class, $product->category);
        $this->assertEquals($category->id, $product->category->id);
    }

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

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

        $this->assertNotNull($product->taxConfiguration);
        $this->assertInstanceOf(CoreTaxConfiguration::class, $product->taxConfiguration);
        $this->assertEquals($tax->id, $product->taxConfiguration->id);
    }

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

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

        $this->assertNotNull($product->tax);
        $this->assertEquals($product->taxConfiguration->id, $product->tax->id);
    }

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

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

        $this->createCoreProductVariant([
            'product_id' => $product->id,
            'name' => 'Variant 1',
        ]);
        $this->createCoreProductVariant([
            'product_id' => $product->id,
            'name' => 'Variant 2',
        ]);

        $product->refresh();

        $this->assertCount(2, $product->variants);
        $this->assertInstanceOf(CoreProductVariant::class, $product->variants->first());
    }

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

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

        $this->createCorePricingRule([
            'product_id' => $product->id,
            'min_quantity' => 1,
            'max_quantity' => 10,
        ]);
        $this->createCorePricingRule([
            'product_id' => $product->id,
            'min_quantity' => 11,
            'max_quantity' => 50,
        ]);

        $product->refresh();

        $this->assertCount(2, $product->pricingRules);
        $this->assertInstanceOf(CorePricingRule::class, $product->pricingRules->first());
    }

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

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

        // This should not throw an error even if WMSInventoryCore is disabled
        $inventories = $product->inventories;

        $this->assertCount(0, $inventories);
    }

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

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

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

        $product->delete();

        $this->assertSoftDeleted('core_products', ['id' => $productId]);
    }

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

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

        $product->delete();
        $this->assertSoftDeleted('core_products', ['id' => $productId]);

        $product->restore();
        $this->assertDatabaseHas('core_products', [
            'id' => $productId,
            'deleted_at' => null,
        ]);
    }

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

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

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

        $codes = array_map(fn ($p) => $p->product_code, $products);
        $uniqueCodes = array_unique($codes);

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

    // ==========================================
    // Trackable Product Tests
    // ==========================================

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

        $trackableProduct = $this->createCoreProduct([
            'is_trackable' => true,
            'created_by_id' => $admin->id,
        ]);

        $nonTrackableProduct = $this->createCoreProduct([
            'is_trackable' => false,
            'created_by_id' => $admin->id,
        ]);

        $this->assertTrue($trackableProduct->is_trackable);
        $this->assertFalse($nonTrackableProduct->is_trackable);
    }
}
