<?php

declare(strict_types=1);

namespace Tests\Feature;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Maestro\Core\Database\Seeders\ModuleAndNavigationSeeder;
use Maestro\Core\Database\Seeders\UserSeeder;
use Maestro\Core\Support\ModuleDependencyMap;
use Tests\Fixtures\AuthTestUser;
use Tests\TestCase;

final class ModuleActivationMigrationWorkflowTest extends TestCase
{
    use RefreshDatabase;

    /**
     * Keep this map in sync with ModuleDependencyMap for all installable modules.
     *
     * @var array<string, array{migration_rows:list<string>,drop_tables:list<string>,ready_table:string,ready_column?:string}>
     */
    private const MODULE_CASES = [
        'offers' => [
            'migration_rows' => [
                '2026_05_03_100000_create_maestro_offer_settings_table',
                '2026_05_03_101000_create_offer_service_catalog_items_table',
                '2026_05_03_102000_create_offers_table',
                '2026_05_03_103000_create_offer_sections_table',
                '2026_05_03_104000_create_offer_rows_table',
                '2026_05_04_200000_offer_sections_hierarchy',
                '2026_05_04_210000_add_offer_document_backgrounds',
            ],
            'drop_tables' => [
                'offer_rows',
                'offer_sections',
                'offers',
                'offer_service_catalog_items',
                'maestro_offer_settings',
            ],
            'ready_table' => 'offers',
            'ready_column' => 'document_background_url',
        ],
        'hours' => [
            'migration_rows' => [
                '2026_05_02_102000_create_hour_internal_categories_table',
                '2026_05_02_103000_create_hour_projects_table',
                '2026_05_02_104000_create_hour_tasks_table',
                '2026_05_02_105000_create_hour_timesheet_weeks_table',
                '2026_05_02_106000_create_hour_timesheet_lines_table',
                '2026_05_03_120000_add_description_to_hour_timesheet_lines_table',
            ],
            'drop_tables' => [
                'hour_timesheet_lines',
                'hour_tasks',
                'hour_projects',
                'hour_timesheet_weeks',
                'hour_internal_categories',
            ],
            'ready_table' => 'hour_timesheet_weeks',
        ],
        'invoices' => [
            'migration_rows' => [
                '2026_05_04_140000_create_maestro_vat_rates_table',
                '2026_05_04_150000_create_invoices_table',
                '2026_05_04_150001_create_invoice_lines_table',
            ],
            'drop_tables' => [
                'invoice_lines',
                'invoices',
                'maestro_vat_rates',
            ],
            'ready_table' => 'invoices',
        ],
        'items' => [
            'migration_rows' => [
                '2026_05_04_145000_create_maestro_items_table',
            ],
            'drop_tables' => [
                'maestro_items',
            ],
            'ready_table' => 'maestro_items',
        ],
    ];

    public function test_can_enable_installation_before_tables_exist_then_migrate_successfully_for_offers(): void
    {
        $this->assertModuleLifecycleWorkflow('offers');
    }

    public function test_can_enable_installation_before_tables_exist_then_migrate_successfully_for_hours(): void
    {
        $this->assertModuleLifecycleWorkflow('hours');
    }

    public function test_can_enable_installation_before_tables_exist_then_migrate_successfully_for_invoices(): void
    {
        $this->assertModuleLifecycleWorkflow('invoices');
    }

    public function test_can_enable_installation_before_tables_exist_then_migrate_successfully_for_items(): void
    {
        $this->assertModuleLifecycleWorkflow('items');
    }

    public function test_all_non_foundational_modules_have_lifecycle_workflow_case(): void
    {
        $expected = array_values(array_filter(
            ModuleDependencyMap::allSlugs(),
            fn (string $slug): bool => $slug !== 'customers',
        ));
        sort($expected);

        $actual = array_keys(self::MODULE_CASES);
        sort($actual);

        self::assertSame(
            $expected,
            $actual,
            'When adding a new module to ModuleDependencyMap, add lifecycle workflow coverage to this test.',
        );
    }

    public function test_runtime_toggle_requires_install_toggle_first(): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $this->resetModuleState('offers');

        $this->actingAs($user, 'web')
            ->patchJson('/api/superadmin/modules/offers/runtime', ['is_enabled' => true])
            ->assertStatus(422);

        $this->actingAs($user, 'web')
            ->patchJson('/api/superadmin/modules/offers/installation', ['is_enabled' => true])
            ->assertOk();

        $this->artisan('migrate', ['--force' => true])->assertExitCode(0);

        $this->actingAs($user, 'web')
            ->patchJson('/api/superadmin/modules/offers/runtime', ['is_enabled' => true])
            ->assertOk()
            ->assertJsonPath('data.is_runtime_enabled', true);
    }

    public function test_install_toggle_cannot_be_turned_off_after_migration_exists(): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $this->actingAs($user, 'web')
            ->patchJson('/api/superadmin/modules/offers/installation', ['is_enabled' => true])
            ->assertOk();

        self::assertTrue(Schema::hasTable('offers'));

        $this->actingAs($user, 'web')
            ->patchJson('/api/superadmin/modules/offers/installation', ['is_enabled' => false])
            ->assertStatus(422);
    }

    private function assertModuleLifecycleWorkflow(string $slug): void
    {
        $this->seed([UserSeeder::class, ModuleAndNavigationSeeder::class]);

        $user = AuthTestUser::query()->where('email', 'demo@example.com')->first();
        self::assertNotNull($user);

        $this->resetModuleState($slug);
        $case = self::MODULE_CASES[$slug];

        self::assertFalse(Schema::hasTable($case['ready_table']));

        $this->actingAs($user, 'web')
            ->patchJson("/api/superadmin/modules/{$slug}/installation", ['is_enabled' => true])
            ->assertOk()
            ->assertJsonPath('data.slug', $slug)
            ->assertJsonPath('data.is_install_enabled', true);

        self::assertTrue((bool) DB::table('maestro_modules')->where('slug', $slug)->value('is_install_enabled'));

        $this->artisan('migrate', ['--force' => true])->assertExitCode(0);

        self::assertTrue(Schema::hasTable($case['ready_table']));
        if (isset($case['ready_column'])) {
            self::assertTrue(Schema::hasColumn($case['ready_table'], $case['ready_column']));
        }

        $this->actingAs($user, 'web')
            ->patchJson("/api/superadmin/modules/{$slug}/installation", ['is_enabled' => false])
            ->assertStatus(422);
    }

    private function resetModuleState(string $slug): void
    {
        $case = self::MODULE_CASES[$slug];

        DB::table('migrations')
            ->whereIn('migration', $case['migration_rows'])
            ->delete();

        Schema::disableForeignKeyConstraints();
        try {
            foreach ($case['drop_tables'] as $table) {
                Schema::dropIfExists($table);
            }
        } finally {
            Schema::enableForeignKeyConstraints();
        }
    }
}
