<?php

namespace Tests\Feature\Catalog;

use App\Domains\Catalog\Models\Attribute;
use App\Domains\Catalog\Models\Product;
use App\Domains\Catalog\Models\ProductVariant;
use App\Domains\Tenant\Models\Tenant;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Str;
use Tests\TestCase;

class ProductControllerTest extends TestCase
{
    use RefreshDatabase;

    private Tenant $tenant;
    private Attribute $sizeAttr;
    private Attribute $colorAttr;
    private Attribute $extraAttr;

    protected function setUp(): void
    {
        parent::setUp();

        $this->tenant = $this->createTenant('acme', 'Acme Inc.', 'acme.com');

        // Create attributes for variants
        $this->sizeAttr = Attribute::create([
            'tenant_codigo' => $this->tenant->codigo,
            'codigo' => 'talle',
            'nombre' => 'Talle',
            'is_required' => true,
            'type' => 'select',
        ]);
        $this->sizeAttr->options()->createMany([
            ['value' => 'S', 'label' => 'S'],
            ['value' => '38', 'label' => '38'],
        ]);

        $this->colorAttr = Attribute::create([
            'tenant_codigo' => $this->tenant->codigo,
            'codigo' => 'color',
            'nombre' => 'Color',
            'is_required' => true,
            'type' => 'select',
        ]);
        $this->colorAttr->options()->createMany([
            ['value' => 'Azul', 'label' => 'Azul'],
            ['value' => 'Rojo', 'label' => 'Rojo'],
        ]);

        $this->extraAttr = Attribute::create([
            'tenant_codigo' => $this->tenant->codigo,
            'codigo' => 'extra',
            'nombre' => 'Extra Attribute',
            'is_required' => false,
            'type' => 'string',
        ]);
    }

    public function test_it_creates_product_with_attributes_and_then_creates_variants(): void
    {
        $payload = [
            'categoria_id' => 1,
            'slug' => 'remera-sport',
            'nombre' => 'Remera Sport',
            'descripcion' => 'Remera para hacer deportes',
            'precio' => 15000.00,
            'attribute_ids' => [
                $this->extraAttr->id,
                $this->sizeAttr->id,
                $this->colorAttr->id,
            ],
        ];

        $response = $this->postJson("/api/tenants/{$this->tenant->codigo}/productos", $payload);

        $response->assertCreated();

        // Assert JSON structure
        $response->assertJsonPath('data.nombre', 'Remera Sport');

        // Assert Database
        $this->assertDatabaseHas('productos', [
            'tenant_codigo' => $this->tenant->codigo,
            'slug' => 'remera-sport',
        ]);

        $product = Product::where('slug', 'remera-sport')->firstOrFail();

        // Assert that products_attributes has both explicit extraAttr and those from variants (sizeAttr, colorAttr)
        $this->assertDatabaseHas('products_attributes', [
            'product_id' => $product->id,
            'attribute_id' => $this->extraAttr->id,
        ]);
        $this->assertDatabaseHas('products_attributes', [
            'product_id' => $product->id,
            'attribute_id' => $this->sizeAttr->id,
        ]);
        $this->assertDatabaseHas('products_attributes', [
            'product_id' => $product->id,
            'attribute_id' => $this->colorAttr->id,
        ]);

        // Create a variant
        $variantPayload = [
            'producto_id' => $product->id,
            'slug' => 'remera-sport-s-azul',
            'nombre' => 'Remera Sport S Azul',
            'stock' => 10,
            'precio' => 15000.00,
            'definitions' => [
                [
                    'attribute_id' => $this->sizeAttr->id,
                    'value' => 'S',
                ],
                [
                    'attribute_id' => $this->colorAttr->id,
                    'value' => 'Azul',
                ],
            ],
        ];

        $variantResponse = $this->postJson("/api/tenants/{$this->tenant->codigo}/product-variants", $variantPayload);
        $variantResponse->assertCreated();

        $this->assertDatabaseHas('productos_variantes', [
            'producto_id' => $product->id,
            'slug' => 'remera-sport-s-azul',
            'stock' => 10,
            'precio' => 15000.00,
        ]);

        $variantS = ProductVariant::where('slug', 'remera-sport-s-azul')->firstOrFail();
        $this->assertDatabaseHas('productos_variantes_values', [
            'producto_variante_id' => $variantS->id,
            'attribute_id' => $this->sizeAttr->id,
            'value' => 'S',
        ]);
        $this->assertDatabaseHas('productos_variantes_values', [
            'producto_variante_id' => $variantS->id,
            'attribute_id' => $this->colorAttr->id,
            'value' => 'Azul',
        ]);
    }

    public function test_it_updates_product_attributes_independently(): void
    {
        // 1. Create a product with extraAttr
        $product = Product::create([
            'tenant_codigo' => $this->tenant->codigo,
            'categoria_id' => 1,
            'slug' => 'pantalon-cargo',
            'nombre' => 'Pantalon Cargo',
            'precio' => 20000.00,
        ]);

        $product->attributes()->sync([$this->extraAttr->id]);

        // 2. Perform update payload - change name and update attribute_ids to sizeAttr
        $payload = [
            'categoria_id' => 1,
            'slug' => 'pantalon-cargo-new-slug',
            'nombre' => 'Pantalon Cargo V2',
            'precio' => 22000.00,
            'attribute_ids' => [$this->sizeAttr->id],
        ];

        $response = $this->putJson(
            "/api/tenants/{$this->tenant->codigo}/productos/{$product->id}",
            $payload
        );

        $response->assertOk();

        // Assert updated values
        $response->assertJsonPath('data.nombre', 'Pantalon Cargo V2');

        // Check DB state
        // extraAttr must be detached
        $this->assertDatabaseMissing('products_attributes', [
            'product_id' => $product->id,
            'attribute_id' => $this->extraAttr->id,
        ]);
        // sizeAttr must be attached
        $this->assertDatabaseHas('products_attributes', [
            'product_id' => $product->id,
            'attribute_id' => $this->sizeAttr->id,
        ]);
    }

    public function test_it_rejects_variant_creation_with_attributes_not_associated_with_product(): void
    {
        // Create product with only sizeAttr associated
        $product = Product::create([
            'tenant_codigo' => $this->tenant->codigo,
            'categoria_id' => 1,
            'slug' => 'pantalon-cargo',
            'nombre' => 'Pantalon Cargo',
            'precio' => 20000.00,
        ]);
        $product->attributes()->sync([$this->sizeAttr->id]);

        // Attempt to create variant with colorAttr (which is not associated with the product)
        $variantPayload = [
            'producto_id' => $product->id,
            'slug' => 'pantalon-cargo-38-rojo',
            'nombre' => 'Pantalon Cargo 38 Rojo',
            'stock' => 5,
            'precio' => 20000.00,
            'definitions' => [
                [
                    'attribute_id' => $this->sizeAttr->id,
                    'value' => '38',
                ],
                [
                    'attribute_id' => $this->colorAttr->id, // not associated!
                    'value' => 'Rojo',
                ],
            ],
        ];

        $response = $this->postJson("/api/tenants/{$this->tenant->codigo}/product-variants", $variantPayload);

        $response->assertUnprocessable();
        $response->assertJsonValidationErrors(['definitions.1.attribute_id']);
    }

    public function test_it_rejects_variant_update_with_attributes_not_associated_with_product(): void
    {
        // Create product with only sizeAttr associated
        $product = Product::create([
            'tenant_codigo' => $this->tenant->codigo,
            'categoria_id' => 1,
            'slug' => 'pantalon-cargo',
            'nombre' => 'Pantalon Cargo',
            'precio' => 20000.00,
        ]);
        $product->attributes()->sync([$this->sizeAttr->id]);

        $variant = $product->variants()->create([
            'slug' => 'pantalon-cargo-38',
            'nombre' => 'Pantalon Cargo 38',
            'stock' => 5,
            'precio' => 20000.00,
        ]);
        $variant->definitions()->create([
            'attribute_id' => $this->sizeAttr->id,
            'value' => '38',
        ]);

        // Attempt to update variant with colorAttr (which is not associated with the product)
        $variantPayload = [
            'producto_id' => $product->id,
            'slug' => 'pantalon-cargo-38-rojo',
            'nombre' => 'Pantalon Cargo 38 Rojo',
            'stock' => 5,
            'precio' => 20000.00,
            'definitions' => [
                [
                    'attribute_id' => $this->sizeAttr->id,
                    'value' => '38',
                ],
                [
                    'attribute_id' => $this->colorAttr->id, // not associated!
                    'value' => 'Rojo',
                ],
            ],
        ];

        $response = $this->putJson("/api/tenants/{$this->tenant->codigo}/product-variants/{$variant->id}", $variantPayload);

        $response->assertUnprocessable();
        $response->assertJsonValidationErrors(['definitions.1.attribute_id']);
    }

    public function test_it_does_not_modify_variants_if_not_present_in_update_payload(): void
    {
        $product = Product::create([
            'tenant_codigo' => $this->tenant->codigo,
            'categoria_id' => 1,
            'slug' => 'short-running',
            'nombre' => 'Short Running',
            'precio' => 8000.00,
        ]);

        $v1 = $product->variants()->create([
            'slug' => 'short-running-m',
            'nombre' => 'Short Running M',
            'stock' => 5,
            'precio' => 8000.00,
        ]);

        $payload = [
            'categoria_id' => 1,
            'slug' => 'short-running',
            'nombre' => 'Short Running Updated',
            'precio' => 9000.00,
        ];

        $response = $this->putJson(
            "/api/tenants/{$this->tenant->codigo}/productos/{$product->id}",
            $payload
        );

        $response->assertOk();
        $this->assertDatabaseHas('productos', [
            'id' => $product->id,
            'nombre' => 'Short Running Updated',
        ]);

        // Variant should still exist untouched
        $this->assertDatabaseHas('productos_variantes', [
            'id' => $v1->id,
            'slug' => 'short-running-m',
            'stock' => 5,
        ]);
    }

    public function test_it_rejects_variants_with_invalid_attributes(): void
    {
        $product = Product::create([
            'tenant_codigo' => $this->tenant->codigo,
            'categoria_id' => 1,
            'slug' => 'pantalon-cargo',
            'nombre' => 'Pantalon Cargo',
            'precio' => 20000.00,
        ]);

        $payload = [
            'producto_id' => $product->id,
            'slug' => 'remera-sport-s-azul',
            'nombre' => 'Remera Sport S Azul',
            'stock' => 10,
            'precio' => 15000.00,
            'definitions' => [
                [
                    'attribute_id' => 99999, // Non-existent ID
                    'value' => 'S',
                ],
            ],
        ];

        $response = $this->postJson("/api/tenants/{$this->tenant->codigo}/product-variants", $payload);

        $response->assertUnprocessable();
        $response->assertJsonValidationErrors(['definitions.0.attribute_id']);
    }

    public function test_it_throws_exception_when_variant_value_does_not_belong_to_attribute_options(): void
    {
        // 1. Create a select attribute with options
        $selectAttr = Product::createAttribute($this->tenant, [
            'codigo' => 'tamanho',
            'nombre' => 'Tamanho',
            'type' => 'select',
            'options' => [
                ['value' => 'P', 'label' => 'Piqueno'],
                ['value' => 'M', 'label' => 'Medio'],
            ]
        ]);

        // 2. Create product and associate attribute
        $product = Product::create([
            'tenant_codigo' => $this->tenant->codigo,
            'categoria_id' => 1,
            'slug' => 'test-prod-validation',
            'nombre' => 'Test Prod Validation',
            'precio' => 100.00,
        ]);
        $product->attributes()->sync([$selectAttr->id]);

        // 3. Expect exception when creating variant with invalid value 'G'
        $this->expectException(\InvalidArgumentException::class);
        $this->expectExceptionMessage("The value 'G' is not a valid option for the select attribute 'Tamanho'.");

        $product->createVariant([
            'slug' => 'test-prod-validation-g',
            'nombre' => 'Test Prod Validation G',
            'stock' => 5,
            'precio' => 100.00,
            'definitions' => [
                [
                    'attribute_id' => $selectAttr->id,
                    'value' => 'G', // Invalid value
                ]
            ]
        ]);
    }

    protected function createTenant(string $codigo, string $nombre, string $dominio): Tenant
    {
        $hdrKey = (string) Str::uuid();
        $ftrKey = (string) Str::uuid();

        $headerAttachment = \App\Domains\Attachable\Models\Attachment::create([
            'key' => $hdrKey,
            'path' => 'tenants/' . $hdrKey . '.png',
            'filename' => 'logo_header.png',
            'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
            'mime_type' => 'image/png',
        ]);
        $footerAttachment = \App\Domains\Attachable\Models\Attachment::create([
            'key' => $ftrKey,
            'path' => 'tenants/' . $ftrKey . '.png',
            'filename' => 'logo_footer.png',
            'type' => \App\Domains\Attachable\Enums\AttachmentType::Image,
            'mime_type' => 'image/png',
        ]);

        return Tenant::create([
            'codigo' => $codigo,
            'nombre' => $nombre,
            'dominio' => $dominio,
            'primary_color' => '#111111',
            'secondary_color' => '#222222',
            'danger_color' => '#333333',
            'header_footer_bg_color' => '#444444',
            'header_logo_id' => $headerAttachment->id,
            'footer_logo_id' => $footerAttachment->id,
        ]);
    }
}
