<?php

namespace Tests\Feature\Seeders;

use App\Domains\Attachable\Enums\AttachmentType;
use App\Domains\Attachable\Models\Attachment;
use App\Domains\Authorization\Enums\RoleCode;
use App\Domains\Authorization\Models\Role;
use App\Domains\Menu\Models\Menu;
use App\Domains\Tenant\Models\Tenant;
use Database\Seeders\AuthorizationSeeder;
use Database\Seeders\MenuSeeder;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class MenuSeederTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_seeds_the_admin_menus(): void
    {
        $tenant = $this->createTenant('admin_tenant');
        $fiestaTenant = $this->createTenant('fiesta_futbol_infantil');

        $this->seed(MenuSeeder::class);

        $expectedMenus = [
            'adminapp.inicio' => ['Inicio', '/admin/inicio'],
            'adminapp.catalog' => ['Catálogo', '/admin/catalog'],
            'adminapp.categories' => ['Categorías', '/admin/categories'],
            'adminapp.combos' => ['Combos', '/admin/combos'],
            'adminapp.event' => ['Eventos', '/admin/event'],
            'adminapp.staff' => ['Staff', '/admin/staff'],
            'adminapp.ventas' => ['Ventas', '/admin/ventas'],
        ];
        $fiestaCategoryMenus = [
            'adminapp.fiesta-futbol-infantil.entradas' => ['Entradas', '/admin/entradas'],
            'adminapp.fiesta-futbol-infantil.alojamientos' => ['Alojamientos', '/admin/alojamientos'],
            'adminapp.fiesta-futbol-infantil.merchandising' => ['Merchandising', '/admin/merchandising'],
            'adminapp.fiesta-futbol-infantil.comida' => ['Comida', '/admin/comidas'],
        ];

        $adminApp = Menu::query()
            ->with('children')
            ->where('code', 'main.adminapp')
            ->firstOrFail();

        $this->assertSame('Administración', $adminApp->label);
        $this->assertSame(Menu::CONTENT_TYPE_DYNAMIC, $adminApp->content_type);
        $this->assertSame('/', $adminApp->route);
        $this->assertSame(
            array_keys([...$expectedMenus, ...$fiestaCategoryMenus]),
            $adminApp->children->pluck('code')->sort()->values()->all()
        );
        $this->assertTrue(
            $tenant->menues()->where('menues.code', 'main.adminapp')->exists()
        );

        $adminMenus = Menu::query()
            ->whereIn('code', array_keys($expectedMenus))
            ->get()
            ->keyBy('code');

        $this->assertCount(count($expectedMenus), $adminMenus);

        foreach ($expectedMenus as $code => [$label, $route]) {
            $this->assertSame($label, $adminMenus->get($code)?->label);
            $this->assertSame($route, $adminMenus->get($code)?->route);
            $this->assertSame('main.adminapp', $adminMenus->get($code)?->parent_menu_code);
            $this->assertTrue(
                $tenant->menues()->where('menues.code', $code)->exists()
            );
        }

        foreach ($fiestaCategoryMenus as $code => [$label, $route]) {
            $menu = Menu::query()->where('code', $code)->firstOrFail();

            $this->assertSame($label, $menu->label);
            $this->assertSame($route, $menu->route);
            $this->assertSame('main.adminapp', $menu->parent_menu_code);
            $this->assertFalse($tenant->menues()->where('menues.code', $code)->exists());
            $this->assertTrue($fiestaTenant->menues()->where('menues.code', $code)->exists());
        }

        foreach ([
            'adminapp.inicio',
            'adminapp.catalog',
            'adminapp.categories',
            'adminapp.combos',
        ] as $code) {
            $this->assertFalse(
                $fiestaTenant->menues()->where('menues.code', $code)->exists()
            );
        }

        $this->assertFalse(
            Menu::query()->whereIn('code', [
                'admin.event',
                'admin.catalog',
                'admin.combos',
                'admin.categories',
                'admin.ventas',
                'admin.staff',
                'event',
                'catalog',
                'combos',
                'categories',
                'ventas',
                'staff',
            ])->exists()
        );
    }

    public function test_it_assigns_menus_to_the_expected_roles(): void
    {
        $this->seed(AuthorizationSeeder::class);
        $this->seed(MenuSeeder::class);

        $allMenuCodes = Menu::query()
            ->orderBy('code')
            ->pluck('code')
            ->all();
        $adminAppMenuCodes = Menu::query()
            ->where('code', 'main.adminapp')
            ->orWhere('parent_menu_code', 'main.adminapp')
            ->pluck('code');
        $scannerMenuCodes = Menu::query()
            ->where('code', 'like', 'scanner.%')
            ->orderBy('code')
            ->pluck('code')
            ->all();
        $userMenuCodes = Menu::query()
            ->whereNotIn('code', [...$adminAppMenuCodes, ...$scannerMenuCodes])
            ->orderBy('code')
            ->pluck('code')
            ->all();

        foreach ([RoleCode::Admin->value, RoleCode::AdminApp->value] as $roleCode) {
            $roleMenuCodes = Role::query()
                ->where('codigo', $roleCode)
                ->firstOrFail()
                ->menus()
                ->orderBy('menues.code')
                ->pluck('menues.code')
                ->all();

            $this->assertSame($allMenuCodes, $roleMenuCodes);
        }

        $userRoleMenuCodes = Role::query()
            ->where('codigo', RoleCode::User->value)
            ->firstOrFail()
            ->menus()
            ->orderBy('menues.code')
            ->pluck('menues.code')
            ->all();

        $this->assertSame($userMenuCodes, $userRoleMenuCodes);
        $this->assertNotContains('main.adminapp', $userRoleMenuCodes);
        $this->assertNotContains('adminapp.catalog', $userRoleMenuCodes);

        $scannerRoleMenuCodes = Role::query()
            ->where('codigo', RoleCode::Scanner->value)
            ->firstOrFail()
            ->menus()
            ->orderBy('menues.code')
            ->pluck('menues.code')
            ->all();

        $this->assertSame([
            'scanner.detail',
            'scanner.inicio',
            'scanner.scan',
        ], $scannerRoleMenuCodes);
        $this->assertDatabaseCount(
            'roles_menues',
            (count($allMenuCodes) * 2) + count($userMenuCodes) + count($scannerMenuCodes)
        );
    }

    public function test_it_seeds_the_account_menu_hierarchy(): void
    {
        $tenant = $this->createTenant('account_tenant');

        $this->seed(MenuSeeder::class);

        $account = Menu::query()
            ->with('children')
            ->where('code', 'account')
            ->firstOrFail();

        $this->assertSame(Menu::CONTENT_TYPE_DYNAMIC, $account->content_type);
        $this->assertSame('Mi cuenta', $account->label);
        $this->assertSame('/mi-cuenta', $account->route);
        $this->assertSame([
            'account.profile',
            'account.purchases',
            'account.tickets',
        ], $account->children->pluck('code')->sort()->values()->all());
        $this->assertSame([
            'account.profile' => '/mi-cuenta/datos-personales',
            'account.purchases' => '/mi-cuenta/compras',
            'account.tickets' => '/mi-cuenta/tickets',
        ], $account->children->pluck('route', 'code')->sortKeys()->all());

        $this->assertTrue(
            $tenant->menues()->where('menues.code', 'account')->exists()
        );
        $this->assertFalse(
            Menu::query()->whereIn('code', ['profile', 'purchases', 'tickets'])->exists()
        );
    }

    public function test_it_seeds_the_help_menu_hierarchy_and_content_schemas(): void
    {
        $this->seed(MenuSeeder::class);

        $help = Menu::query()
            ->with('children')
            ->where('code', 'help')
            ->firstOrFail();

        $this->assertSame(Menu::CONTENT_TYPE_DYNAMIC, $help->content_type);
        $this->assertSame('Ayuda', $help->label);
        $this->assertSame([
            'help.contact',
            'help.faq',
            'help.payment-methods',
            'help.shipping',
            'help.terms-and-conditions',
        ], $help->children->pluck('code')->sort()->values()->all());

        $faq = Menu::query()->where('code', 'help.faq')->firstOrFail();
        $this->assertSame(Menu::CONTENT_TYPE_STATIC, $faq->content_type);
        $this->assertEquals([
            '*.pregunta' => 'required|string',
            '*.respuesta' => 'required|string',
            '*.is_active' => 'sometimes|boolean',
        ], $faq->static_content_schema);

        $contact = Menu::query()->where('code', 'help.contact')->firstOrFail();
        $this->assertSame(Menu::CONTENT_TYPE_STATIC, $contact->content_type);
        $this->assertSame('required|url', $contact->static_content_schema['whatsapp.whatsapp_url']);
        $this->assertSame('required|string', $contact->static_content_schema['phone']);
        $this->assertSame(
            'required|array|min:1',
            $contact->static_content_schema['locations.*.addresses']
        );
        $this->assertSame(
            'required|array|size:2',
            $contact->static_content_schema['locations.*.addresses.*.coordinates']
        );
        $this->assertSame(
            'required|string',
            $contact->static_content_schema['locations.*.addresses.*.address']
        );
        $this->assertSame(
            'required|numeric|between:-90,90',
            $contact->static_content_schema['locations.*.addresses.*.coordinates.0']
        );
        $this->assertSame('prohibited', $contact->static_content_schema['map_locations']);
    }

    public function test_it_assigns_help_to_the_supported_tenants_with_faq_content(): void
    {
        $sonder = $this->createTenant('sonder');
        $fiesta = $this->createTenant('fiesta_futbol_infantil');
        $otherTenant = $this->createTenant('other');

        $this->seed(MenuSeeder::class);

        $expectedQuestions = [
            [
                'pregunta' => '¿Hay algún límite de compra?',
                'respuesta' => 'No hay un límite general de compra. La cantidad disponible depende del stock de cada producto.',
                'is_active' => true,
            ],
            [
                'pregunta' => '¿Cuáles son los medios de pago disponibles?',
                'respuesta' => 'Podés consultar y seleccionar los medios de pago habilitados al momento de finalizar tu compra.',
                'is_active' => false,
            ],
            [
                'pregunta' => 'Necesito asesoramiento antes de comprar el producto, ¿cómo puedo hacer?',
                'respuesta' => 'Nuestro equipo comercial está preparado para asesorarte, no dudes en escribirnos por WhatsApp al (0341) 6658247 de lunes a viernes de 8.30 a 20 horas. Los sábados, domingos y feriados te contestaremos lo más rápido posible.',
                'is_active' => false,
            ],
        ];
        $expectedContact = [
            'whatsapp' => [
                'whatsapp_url' => 'https://wa.me/543412602222',
                'whatsapp_label' => 'Chatea con nosotros',
            ],
            'phone' => '+54 9 (0341) 6658247',
            'locations' => [
                'rosario' => [
                    'label' => 'Rosario',
                    'addresses' => [
                        [
                            'label' => 'Gigante de Arroyito',
                            'address' => 'Av. Génova 640, Rosario',
                            'coordinates' => [-32.913997, -60.674567],
                        ],
                        [
                            'label' => 'Telepagos',
                            'address' => 'Rioja 1150, piso 12, dpto. 3, Rosario',
                            'coordinates' => [-32.946820, -60.639320],
                        ],
                    ],
                ],
            ],
        ];

        foreach ([$sonder, $fiesta] as $tenant) {
            $helpMenus = $tenant->menues()
                ->where(fn ($query) => $query
                    ->where('menues.code', 'help')
                    ->orWhere('menues.parent_menu_code', 'help'))
                ->get();

            $this->assertCount(6, $helpMenus);
            $this->assertEquals(
                $expectedQuestions,
                $helpMenus->firstWhere('code', 'help.faq')->pivot->static_content
            );
            $this->assertEquals(
                $expectedContact,
                $helpMenus->firstWhere('code', 'help.contact')->pivot->static_content
            );
        }

        $this->assertFalse(
            $otherTenant->menues()->where('menues.code', 'help')->exists()
        );
    }

    private function createTenant(string $code): Tenant
    {
        $headerLogo = $this->createAttachment("{$code}-header.png");
        $footerLogo = $this->createAttachment("{$code}-footer.png");

        return Tenant::query()->create([
            'codigo' => $code,
            'nombre' => $code,
            'dominio' => "{$code}.example.com",
            'primary_color' => '#111111',
            'secondary_color' => '#222222',
            'danger_color' => '#333333',
            'success_color' => '#444444',
            'header_bg_color' => '#ffffff',
            'footer_bg_color' => '#ffffff',
            'header_logo_id' => $headerLogo->id,
            'footer_logo_id' => $footerLogo->id,
        ]);
    }

    private function createAttachment(string $filename): Attachment
    {
        return Attachment::query()->create([
            'path' => "test/{$filename}",
            'filename' => $filename,
            'type' => AttachmentType::Image,
            'mime_type' => 'image/png',
        ]);
    }
}
