<?php

namespace Tests\Feature\FiestaFutbolInfantil;

use App\Domains\Auth\Models\User;
use App\Domains\Authorization\Enums\RoleCode;
use App\Domains\Catalog\Models\Attribute;
use App\Domains\Catalog\Models\CatalogItem;
use App\Domains\Catalog\Models\Category;
use App\Domains\Catalog\Models\Variant;
use App\Domains\Menu\Models\Menu;
use App\Domains\Shared\Enums\FieldType;
use App\Domains\Tenant\Models\Tenant;
use App\Domains\Tenant\Models\WebsiteType;
use Database\Seeders\AuthorizationSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Laravel\Sanctum\Sanctum;
use Tests\TestCase;

class FoodControllerTest extends TestCase
{
    use RefreshDatabase;

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

        $this->seed(AuthorizationSeeder::class);
        WebsiteType::query()->create([
            'codigo' => 'onticket',
            'nombre' => 'OnTicket',
        ]);
    }

    public function test_authentication_is_required(): void
    {
        $this->postJson('/api/v1/adminapp/tenant/foods', ['variants' => []])
            ->assertUnauthorized();
    }

    public function test_it_creates_one_food_item_with_multiple_variants(): void
    {
        [$tenant, $firstDate, $secondDate] = $this->configuredTenant();
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $this->postJson('/api/v1/adminapp/tenant/foods', [
            'variants' => [
                $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 1700, 10000),
                $this->variantPayload($secondDate->id, 'Cena', 'Vianda', 900, 8000),
            ],
        ])
            ->assertOk()
            ->assertJsonPath('data.name', 'Comida')
            ->assertJsonCount(2, 'data.variants')
            ->assertJsonPath('data.variants.0.schedule', 'Almuerzo')
            ->assertJsonPath('data.variants.0.service', 'Comedor')
            ->assertJsonPath('data.variants.0.stock', 1700)
            ->assertJsonPath('data.variants.0.price', '10000.00')
            ->assertJsonPath('data.variants.1.price', '8000.00');

        $this->getJson('/api/v1/adminapp/tenant/foods')
            ->assertOk()
            ->assertJsonPath('data.name', 'Comida')
            ->assertJsonCount(2, 'data.variants')
            ->assertJsonPath('data.variants.0.schedule', 'Almuerzo')
            ->assertJsonPath('data.variants.1.service', 'Vianda');

        $this->assertDatabaseCount('catalog_items', 1);
        $this->assertDatabaseCount('variantes', 2);
        $this->assertDatabaseCount('inventories', 2);
        $this->assertDatabaseCount('variant_event_dates', 2);
        $this->assertDatabaseCount('variant_values', 4);
        $food = CatalogItem::query()->where('slug', 'comida')->sole();
        $this->assertDatabaseHas('item_attributes', [
            'catalog_item_id' => $food->id,
            'attribute_id' => Attribute::query()->where('codigo', 'event_date')->sole()->id,
            'sort_order' => 1,
        ]);
        $this->assertDatabaseHas('item_attributes', [
            'catalog_item_id' => $food->id,
            'attribute_id' => Attribute::query()->where('codigo', 'horario')->sole()->id,
            'sort_order' => 2,
        ]);
        $this->assertDatabaseHas('item_attributes', [
            'catalog_item_id' => $food->id,
            'attribute_id' => Attribute::query()->where('codigo', 'servicio')->sole()->id,
            'sort_order' => 3,
        ]);
        $this->assertDatabaseHas('catalog_items', [
            'tenant_code' => $tenant->codigo,
            'slug' => 'comida',
            'nombre' => 'Comida',
            'has_tickets' => true,
            'category_id' => Category::query()
                ->where('tenant_code', $tenant->codigo)
                ->where('nombre', 'Comidas')
                ->sole()
                ->id,
            'precio' => 8000,
        ]);
    }

    public function test_it_updates_variants_with_an_id_and_creates_variants_without_one(): void
    {
        [$tenant, $firstDate, $secondDate] = $this->configuredTenant();
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $created = $this->postJson('/api/v1/adminapp/tenant/foods', [
            'variants' => [
                $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 100, 10000),
            ],
        ])->assertOk();
        $variantId = $created->json('data.variants.0.id');

        $updated = $this->variantPayload($secondDate->id, 'Cena', 'Vianda', 80, 8500);
        $updated['id'] = $variantId;
        $updated['description'] = 'Cena para llevar';

        $this->postJson('/api/v1/adminapp/tenant/foods', [
            'variants' => [
                $updated,
                $this->variantPayload($firstDate->id, 'Almuerzo', 'Vianda', 50, 9000),
            ],
        ])
            ->assertOk()
            ->assertJsonCount(2, 'data.variants')
            ->assertJsonPath('data.variants.0.id', $variantId)
            ->assertJsonPath('data.variants.0.event_date_id', $secondDate->id)
            ->assertJsonPath('data.variants.0.schedule', 'Cena')
            ->assertJsonPath('data.variants.0.service', 'Vianda')
            ->assertJsonPath('data.variants.0.description', 'Cena para llevar')
            ->assertJsonPath('data.variants.0.stock', 80)
            ->assertJsonPath('data.variants.0.price', '8500.00');

        $this->assertDatabaseCount('catalog_items', 1);
        $this->assertDatabaseCount('variantes', 2);
        $this->assertDatabaseHas('variantes', [
            'id' => $variantId,
            'event_date_id' => $secondDate->id,
            'descripcion' => 'Cena para llevar',
            'precio' => 8500,
        ]);
    }

    public function test_it_deletes_food_records_and_removes_the_empty_product(): void
    {
        [$tenant, $firstDate, $secondDate] = $this->configuredTenant();
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $created = $this->postJson('/api/v1/adminapp/tenant/foods', [
            'variants' => [
                $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 100, 10000),
                $this->variantPayload($secondDate->id, 'Cena', 'Vianda', 50, 8000),
            ],
        ])->assertOk();
        $firstId = $created->json('data.variants.0.id');
        $secondId = $created->json('data.variants.1.id');

        $this->deleteJson("/api/v1/adminapp/tenant/foods/{$secondId}")
            ->assertNoContent();

        $this->assertDatabaseHas('catalog_items', [
            'tenant_code' => $tenant->codigo,
            'slug' => 'comida',
            'precio' => 10000,
        ]);
        $this->assertSoftDeleted('variantes', ['id' => $secondId]);
        $this->assertSame(1, Variant::query()->count());
        $this->assertDatabaseCount('inventories', 2);

        $this->deleteJson("/api/v1/adminapp/tenant/foods/{$firstId}")
            ->assertNoContent();

        $this->assertSoftDeleted('variantes', ['id' => $firstId]);
        $this->assertSame(0, CatalogItem::query()->count());
        $this->assertSame(0, Variant::query()->count());
        $this->assertDatabaseCount('inventories', 2);
    }

    public function test_it_rejects_duplicate_combinations(): void
    {
        [$tenant, $firstDate] = $this->configuredTenant();
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $this->postJson('/api/v1/adminapp/tenant/foods', [
            'variants' => [
                $this->variantPayload($firstDate->id, 'Almuerzo', 'Comedor', 100, 10000),
                $this->variantPayload($firstDate->id, ' almuerzo ', ' comedor ', 100, 10000),
            ],
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['variants.1']);

        $this->assertDatabaseCount('catalog_items', 0);
    }

    /** @return array{Tenant, mixed, mixed} */
    private function configuredTenant(): array
    {
        $tenant = Tenant::query()->create([
            'codigo' => 'fiesta_futbol_infantil',
            'nombre' => 'Fiesta Fútbol Infantil',
            'dominio' => 'fiesta.test',
            'website_type_code' => 'onticket',
        ]);
        $eventDateAttribute = Attribute::query()->create([
            'tenant_codigo' => $tenant->codigo,
            'codigo' => 'event_date',
            'nombre' => 'Fecha',
            'type' => FieldType::EventDate,
            'is_required' => true,
        ]);
        $scheduleAttribute = Attribute::query()->create([
            'tenant_codigo' => $tenant->codigo,
            'codigo' => 'horario',
            'nombre' => 'Horario',
            'type' => FieldType::Select,
            'is_required' => true,
        ]);
        $scheduleAttribute->options()->createMany([
            ['value' => 'Almuerzo', 'label' => 'Almuerzo', 'sort_order' => 1],
            ['value' => 'Cena', 'label' => 'Cena', 'sort_order' => 2],
        ]);
        $serviceAttribute = Attribute::query()->create([
            'tenant_codigo' => $tenant->codigo,
            'codigo' => 'servicio',
            'nombre' => 'Servicio',
            'type' => FieldType::Select,
            'is_required' => true,
        ]);
        $serviceAttribute->options()->createMany([
            ['value' => 'Comedor', 'label' => 'Comedor', 'sort_order' => 1],
            ['value' => 'Vianda', 'label' => 'Vianda', 'sort_order' => 2],
        ]);
        $firstDate = $tenant->eventDates()->create([
            'date' => '2026-10-09',
            'time_start' => '00:00',
            'time_end' => '23:59',
        ]);
        $secondDate = $tenant->eventDates()->create([
            'date' => '2026-10-10',
            'time_start' => '00:00',
            'time_end' => '23:59',
        ]);
        $menu = Menu::query()->create([
            'code' => 'adminapp.fiesta-futbol-infantil.comida',
            'label' => 'Comida',
            'route' => '/admin/comidas',
        ]);
        $tenant->menues()->attach($menu->code);

        $this->assertNotNull($eventDateAttribute);

        return [$tenant, $firstDate, $secondDate];
    }

    /** @return array<string, mixed> */
    private function variantPayload(
        int $eventDateId,
        string $schedule,
        string $service,
        int $stock,
        float $price,
    ): array {
        return [
            'event_date_id' => $eventDateId,
            'schedule' => $schedule,
            'service' => $service,
            'description' => null,
            'stock' => $stock,
            'price' => $price,
        ];
    }

    private function createAdminAppUser(Tenant $tenant): User
    {
        return User::factory()->create([
            'rol_codigo' => RoleCode::AdminApp->value,
            'tenant_codigo' => $tenant->codigo,
        ]);
    }
}
