<?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 EntryControllerTest 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/entries', ['entries' => []])
            ->assertUnauthorized();
    }

    public function test_category_visibility_can_be_read_and_updated_for_every_fiesta_menu(): void
    {
        $tenant = $this->createFiestaTenant();
        $categories = [
            'entries' => ['Entradas', 'adminapp.fiesta-futbol-infantil.entradas'],
            'foods' => ['Comidas', 'adminapp.fiesta-futbol-infantil.comida'],
            'accommodations' => ['Alojamientos', 'adminapp.fiesta-futbol-infantil.alojamientos'],
            'merchandise' => ['Merchandising', 'adminapp.fiesta-futbol-infantil.merchandising'],
        ];

        foreach ($categories as [$label, $menuCode]) {
            $menu = Menu::query()->firstOrCreate(
                ['code' => $menuCode],
                ['label' => $label, 'route' => '/admin'],
            );
            $tenant->menues()->syncWithoutDetaching([$menu->code]);
        }

        Sanctum::actingAs($this->createAdminAppUser($tenant));

        foreach ($categories as $endpoint => [$categoryName]) {
            $this->getJson("/api/v1/adminapp/tenant/{$endpoint}/visibility")
                ->assertOk()
                ->assertJsonPath('data.is_enabled', true);

            $this->patchJson("/api/v1/adminapp/tenant/{$endpoint}/visibility", [
                'is_enabled' => false,
            ])
                ->assertOk()
                ->assertJsonPath('data.is_enabled', false)
                ->assertJsonPath(
                    'message',
                    'Mostrar en sitio web se desactivo correctamente',
                );

            $this->patchJson("/api/v1/adminapp/tenant/{$endpoint}/visibility", [
                'is_enabled' => true,
            ])
                ->assertOk()
                ->assertJsonPath('data.is_enabled', true)
                ->assertJsonPath(
                    'message',
                    'Mostrar en sitio web se activo correctamente',
                );

            $this->assertDatabaseHas('categorias', [
                'tenant_code' => $tenant->codigo,
                'nombre' => $categoryName,
                'is_enabled' => true,
            ]);
        }
    }

    public function test_it_creates_multiple_entries_with_dates_and_tracked_inventory(): void
    {
        $tenant = $this->createFiestaTenant();
        $this->createEventDateAttribute($tenant);
        $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',
        ]);
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [
                [
                    'title' => 'Abono',
                    'description' => 'Acceso para ambas jornadas',
                    'event_date_ids' => [$firstDate->id, $secondDate->id],
                    'stock' => 1000,
                    'price' => 10000,
                ],
                [
                    'title' => 'Entrada diaria',
                    'description' => null,
                    'event_date_ids' => [$firstDate->id],
                    'stock' => 250,
                    'price' => 5000.50,
                ],
            ],
        ])
            ->assertOk()
            ->assertJsonCount(2, 'data')
            ->assertJsonPath('data.0.title', 'Abono')
            ->assertJsonPath('data.0.event_date_ids', [$firstDate->id, $secondDate->id])
            ->assertJsonPath('data.0.stock', 1000)
            ->assertJsonPath('data.0.price', '10000.00')
            ->assertJsonPath('data.1.title', 'Entrada diaria')
            ->assertJsonPath('data.1.price', '5000.50');

        $this->getJson('/api/v1/adminapp/tenant/entries')
            ->assertOk()
            ->assertJsonCount(2, 'data')
            ->assertJsonPath('data.0.title', 'Abono')
            ->assertJsonPath('data.0.event_date_ids', [$firstDate->id, $secondDate->id])
            ->assertJsonPath('data.1.title', 'Entrada diaria');

        $this->assertDatabaseCount('catalog_items', 2);
        $this->assertDatabaseCount('variantes', 2);
        $this->assertDatabaseCount('inventories', 2);
        $this->assertDatabaseCount('variant_event_dates', 3);

        $entryCategory = Category::query()
            ->where('tenant_code', $tenant->codigo)
            ->where('nombre', 'Entradas')
            ->sole();

        CatalogItem::query()->orderBy('id')->each(function (CatalogItem $entry) use ($tenant, $entryCategory): void {
            $this->assertSame($tenant->codigo, $entry->tenant_code);
            $this->assertSame($entryCategory->id, $entry->category_id);
            $this->assertSame('tracked', $entry->inventory_policy->value);
            $this->assertTrue($entry->has_tickets);
            $this->assertNull($entry->inventory_id);
        });
    }

    public function test_it_updates_entries_with_an_id_and_creates_entries_without_one(): void
    {
        $tenant = $this->createFiestaTenant();
        $this->createEventDateAttribute($tenant);
        $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',
        ]);
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $createdId = $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [[
                'title' => 'Abono original',
                'description' => null,
                'event_date_ids' => [$firstDate->id],
                'stock' => 10,
                'price' => 100,
            ]],
        ])->assertOk()->json('data.0.id');

        $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [
                [
                    'id' => $createdId,
                    'title' => 'Abono actualizado',
                    'description' => 'Ahora incluye ambas fechas',
                    'event_date_ids' => [$firstDate->id, $secondDate->id],
                    'stock' => 20,
                    'price' => 250,
                ],
                [
                    'title' => 'Entrada nueva',
                    'description' => null,
                    'event_date_ids' => [$secondDate->id],
                    'stock' => 30,
                    'price' => 300,
                ],
            ],
        ])
            ->assertOk()
            ->assertJsonCount(2, 'data')
            ->assertJsonPath('data.0.id', $createdId)
            ->assertJsonPath('data.0.title', 'Abono actualizado')
            ->assertJsonPath('data.0.event_date_ids', [$firstDate->id, $secondDate->id])
            ->assertJsonPath('data.0.stock', 20)
            ->assertJsonPath('data.1.title', 'Entrada nueva');

        $this->assertDatabaseCount('catalog_items', 2);
        $this->assertDatabaseHas('catalog_items', [
            'id' => $createdId,
            'nombre' => 'Abono actualizado',
            'precio' => 250,
        ]);
    }

    public function test_it_deletes_an_entry_and_its_inventory(): void
    {
        $tenant = $this->createFiestaTenant();
        $this->createEventDateAttribute($tenant);
        $eventDate = $tenant->eventDates()->create([
            'date' => '2026-10-09',
            'time_start' => '00:00',
            'time_end' => '23:59',
        ]);
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $entryId = $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [[
                'title' => 'Entrada diaria',
                'description' => null,
                'event_date_ids' => [$eventDate->id],
                'stock' => 100,
                'price' => 5000,
            ]],
        ])->assertOk()->json('data.0.id');

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

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

    public function test_dates_must_belong_to_the_authenticated_tenant(): void
    {
        $tenant = $this->createFiestaTenant();
        $otherTenant = $this->createTenant('other');
        $this->createEventDateAttribute($tenant);
        $foreignDate = $otherTenant->eventDates()->create([
            'date' => '2026-10-09',
            'time_start' => '00:00',
            'time_end' => '23:59',
        ]);
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [[
                'title' => 'Entrada inválida',
                'description' => null,
                'event_date_ids' => [$foreignDate->id],
                'stock' => 10,
                'price' => 100,
            ]],
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['entries.0.event_date_ids.0']);

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

    public function test_entries_are_identified_by_the_entries_category(): void
    {
        $tenant = $this->createFiestaTenant();
        $this->createEventDateAttribute($tenant);
        $eventDate = $tenant->eventDates()->create([
            'date' => '2026-10-09',
            'time_start' => '00:00',
            'time_end' => '23:59',
        ]);
        $merchandisingCategory = Category::query()->create([
            'tenant_code' => $tenant->codigo,
            'nombre' => 'Merchandising',
        ]);
        $merchandise = CatalogItem::query()->create([
            'tenant_code' => $tenant->codigo,
            'category_id' => $merchandisingCategory->id,
            'slug' => 'camiseta',
            'nombre' => 'Camiseta',
            'precio' => 100,
        ]);
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $this->getJson('/api/v1/adminapp/tenant/entries')
            ->assertOk()
            ->assertJsonCount(0, 'data');

        $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [[
                'id' => $merchandise->id,
                'title' => 'Entrada inválida',
                'description' => null,
                'event_date_ids' => [$eventDate->id],
                'stock' => 10,
                'price' => 100,
            ]],
        ])
            ->assertUnprocessable()
            ->assertJsonValidationErrors(['entries.0.id']);
    }

    public function test_the_endpoint_is_only_available_for_fiesta_futbol_infantil(): void
    {
        $tenant = $this->createTenant('other');
        $this->createEventDateAttribute($tenant);
        $eventDate = $tenant->eventDates()->create([
            'date' => '2026-10-09',
            'time_start' => '00:00',
            'time_end' => '23:59',
        ]);
        Sanctum::actingAs($this->createAdminAppUser($tenant));

        $this->postJson('/api/v1/adminapp/tenant/entries', [
            'entries' => [[
                'title' => 'Entrada',
                'description' => null,
                'event_date_ids' => [$eventDate->id],
                'stock' => 10,
                'price' => 100,
            ]],
        ])->assertNotFound();
    }

    private function createFiestaTenant(): Tenant
    {
        $tenant = $this->createTenant('fiesta_futbol_infantil');
        $menu = Menu::query()->create([
            'code' => 'adminapp.fiesta-futbol-infantil.entradas',
            'label' => 'Entradas',
            'route' => '/admin/entradas',
        ]);
        $tenant->menues()->attach($menu->code);

        return $tenant;
    }

    private function createTenant(string $code): Tenant
    {
        return Tenant::query()->create([
            'codigo' => $code,
            'nombre' => ucfirst($code),
            'dominio' => "{$code}.test",
            'website_type_code' => 'onticket',
        ]);
    }

    private function createEventDateAttribute(Tenant $tenant): void
    {
        Attribute::query()->create([
            'tenant_codigo' => $tenant->codigo,
            'codigo' => 'event_date',
            'nombre' => 'Fecha',
            'type' => FieldType::EventDate,
            'is_required' => true,
        ]);
    }

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