<?php

namespace Tests\Feature\Cart;

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

class CartControllerTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_returns_an_empty_guest_cart_when_cart_does_not_exist(): void
    {
        $this->createTenant('acme', 'Acme', 'acme.com');

        $this->getJson('/api/tenants/acme/cart')
            ->assertOk()
            ->assertJson([
                'id' => null,
                'tenant_codigo' => 'acme',
                'status' => 'active',
                'items' => [],
                'subtotal' => '0.00',
            ]);
    }

    public function test_it_creates_a_guest_cart_and_returns_the_cart_snapshot(): void
    {
        $variant = $this->createVariantForTenant('acme', 10, '49.90');
        $attribute = Attribute::query()->create([
            'tenant_codigo' => 'acme',
            'codigo' => 'color',
            'nombre' => 'Color',
            'type' => 'string',
        ]);

        ProductVariantDefinition::query()->create([
            'producto_variante_id' => $variant->id,
            'attribute_id' => $attribute->id,
            'value' => 'Red',
        ]);

        $response = $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $variant->id,
            'cantidad' => 2,
        ]);

        $response
            ->assertOk()
            ->assertCookie('guest_token')
            ->assertJsonPath('tenant_codigo', 'acme')
            ->assertJsonPath('items.0.cantidad', 2)
            ->assertJsonPath('items.0.precio_unitario', '49.90')
            ->assertJsonPath('items.0.subtotal', '99.80')
            ->assertJsonPath('items.0.product.id', $variant->product->id)
            ->assertJsonPath('items.0.variant.id', $variant->id)
            ->assertJsonPath('items.0.variant.definitions.0.attribute.codigo', 'color')
            ->assertJsonPath('subtotal', '99.80');

        $this->assertDatabaseHas('carritos', [
            'tenant_codigo' => 'acme',
            'guest_token' => $response->getCookie('guest_token')?->getValue(),
            'status' => 'active',
        ]);

        $this->assertDatabaseHas('carrito_items', [
            'producto_variante_id' => $variant->id,
            'cantidad' => 2,
        ]);

        $this->assertDatabaseHas('productos_variantes', [
            'id' => $variant->id,
            'stock' => 8,
        ]);
    }

    public function test_it_merges_quantities_when_the_same_guest_adds_the_same_variant_twice(): void
    {
        $variant = $this->createVariantForTenant('acme', 12, '25.00');

        $firstResponse = $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $variant->id,
            'cantidad' => 2,
        ]);

        $guestToken = $firstResponse->getCookie('guest_token')?->getValue();

        $this->withCookie('guest_token', $guestToken)
            ->postJson('/api/tenants/acme/cart/items', [
                'product_variant_id' => $variant->id,
                'cantidad' => 3,
            ])
            ->assertOk()
            ->assertJsonPath('items.0.cantidad', 5)
            ->assertJsonPath('items.0.subtotal', '125.00')
            ->assertJsonPath('subtotal', '125.00');

        $this->assertDatabaseCount('carritos', 1);
        $this->assertDatabaseCount('carrito_items', 1);
        $this->assertDatabaseHas('carrito_items', [
            'producto_variante_id' => $variant->id,
            'cantidad' => 5,
        ]);
        $this->assertDatabaseHas('productos_variantes', [
            'id' => $variant->id,
            'stock' => 7,
        ]);
    }

    public function test_it_updates_item_quantity_and_adjusts_stock(): void
    {
        $variant = $this->createVariantForTenant('acme', 10, '15.00');

        $createResponse = $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $variant->id,
            'cantidad' => 2,
        ]);

        $guestToken = $createResponse->getCookie('guest_token')?->getValue();

        $this->withCookie('guest_token', $guestToken)
            ->patchJson("/api/tenants/acme/cart/items/{$variant->id}", [
                'cantidad' => 5,
            ])
            ->assertOk()
            ->assertJsonPath('items.0.cantidad', 5)
            ->assertJsonPath('items.0.subtotal', '75.00')
            ->assertJsonPath('subtotal', '75.00');

        $this->assertDatabaseHas('carrito_items', [
            'producto_variante_id' => $variant->id,
            'cantidad' => 5,
        ]);
        $this->assertDatabaseHas('productos_variantes', [
            'id' => $variant->id,
            'stock' => 5,
        ]);
    }

    public function test_it_removes_an_item_and_restores_stock(): void
    {
        $variant = $this->createVariantForTenant('acme', 10, '15.00');

        $createResponse = $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $variant->id,
            'cantidad' => 4,
        ]);

        $guestToken = $createResponse->getCookie('guest_token')?->getValue();

        $this->withCookie('guest_token', $guestToken)
            ->deleteJson("/api/tenants/acme/cart/items/{$variant->id}")
            ->assertOk()
            ->assertJsonPath('items', [])
            ->assertJsonPath('subtotal', '0.00');

        $this->assertDatabaseCount('carrito_items', 0);
        $this->assertDatabaseHas('productos_variantes', [
            'id' => $variant->id,
            'stock' => 10,
        ]);
    }

    public function test_authenticated_users_reuse_the_same_cart_per_tenant_and_get_a_new_one_for_another_tenant(): void
    {
        $user = User::factory()->create();
        $acmeVariantA = $this->createVariantForTenant('acme', 10, '10.00');
        $acmeVariantB = $this->createVariantForTenant('acme', 8, '20.00', 'hoodie');
        $globexVariant = $this->createVariantForTenant('globex', 6, '30.00');

        $this->actingAs($user)
            ->postJson('/api/tenants/acme/cart/items', [
                'product_variant_id' => $acmeVariantA->id,
                'cantidad' => 1,
            ])
            ->assertOk();

        $this->actingAs($user)
            ->postJson('/api/tenants/acme/cart/items', [
                'product_variant_id' => $acmeVariantB->id,
                'cantidad' => 2,
            ])
            ->assertOk()
            ->assertJsonPath('subtotal', '50.00');

        $this->actingAs($user)
            ->postJson('/api/tenants/globex/cart/items', [
                'product_variant_id' => $globexVariant->id,
                'cantidad' => 1,
            ])
            ->assertOk();

        $this->assertDatabaseCount('carritos', 2);
        $this->assertDatabaseHas('carritos', [
            'tenant_codigo' => 'acme',
            'user_id' => $user->id,
        ]);
        $this->assertDatabaseHas('carritos', [
            'tenant_codigo' => 'globex',
            'user_id' => $user->id,
        ]);
    }

    public function test_it_rejects_variants_from_another_tenant(): void
    {
        $this->createVariantForTenant('acme', 10, '10.00');
        $otherVariant = $this->createVariantForTenant('globex', 10, '20.00');

        $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $otherVariant->id,
            'cantidad' => 1,
        ])->assertNotFound();
    }

    public function test_it_returns_not_found_when_the_cart_item_does_not_exist_for_update_or_delete(): void
    {
        $variant = $this->createVariantForTenant('acme', 10, '10.00');

        $this->patchJson("/api/tenants/acme/cart/items/{$variant->id}", [
            'cantidad' => 2,
        ])->assertNotFound();

        $this->deleteJson("/api/tenants/acme/cart/items/{$variant->id}")
            ->assertNotFound();
    }

    public function test_it_validates_quantity_and_stock_constraints(): void
    {
        $variant = $this->createVariantForTenant('acme', 2, '10.00');

        $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $variant->id,
            'cantidad' => 0,
        ])->assertUnprocessable()->assertJsonValidationErrors(['cantidad']);

        $response = $this->postJson('/api/tenants/acme/cart/items', [
            'product_variant_id' => $variant->id,
            'cantidad' => 2,
        ]);

        $guestToken = $response->getCookie('guest_token')?->getValue();

        $this->withCookie('guest_token', $guestToken)
            ->postJson('/api/tenants/acme/cart/items', [
                'product_variant_id' => $variant->id,
                'cantidad' => 1,
            ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['cantidad']);

        $this->withCookie('guest_token', $guestToken)
            ->patchJson("/api/tenants/acme/cart/items/{$variant->id}", [
                'cantidad' => 3,
            ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['cantidad']);
    }

    protected function createVariantForTenant(
        string $tenantCode,
        int $stock,
        string $price,
        string $slugPrefix = 'shirt',
    ): ProductVariant {
        $tenant = Tenant::query()->where('codigo', $tenantCode)->first();
        if (! $tenant) {
            $this->createTenant($tenantCode, ucfirst($tenantCode), "{$tenantCode}.com");
        }

        $category = \App\Domains\Catalog\Models\Category::query()->create([
            'tenant_code' => $tenantCode,
            'nombre' => "{$slugPrefix} category {$tenantCode}",
        ]);

        $product = Product::query()->create([
            'tenant_codigo' => $tenantCode,
            'categoria_id' => $category->id,
            'slug' => "{$slugPrefix}-{$tenantCode}-".Product::query()->count(),
            'nombre' => ucfirst($slugPrefix)." {$tenantCode}",
            'descripcion' => 'Test product',
            'precio' => $price,
        ]);

        return ProductVariant::query()->create([
            'producto_id' => $product->id,
            'slug' => "{$slugPrefix}-variant-".ProductVariant::query()->count(),
            'nombre' => ucfirst($slugPrefix).' Variant',
            'stock' => $stock,
            'descripcion' => 'Test variant',
            'precio' => $price,
        ])->load('product');
    }

    protected function createTenant(string $codigo, string $nombre, string $dominio): Tenant
    {
        $hdrKey = (string) \Illuminate\Support\Str::uuid();
        $ftrKey = (string) \Illuminate\Support\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,
        ]);
    }
}
