<?php

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        if (! Schema::hasTable('managed_environment_onboarding_sessions')) {
            return;
        }

        $driver = Schema::getConnection()->getDriverName();

        if ($driver === 'sqlite') {
            Schema::disableForeignKeyConstraints();

            Schema::rename('managed_environment_onboarding_sessions', 'managed_environment_onboarding_sessions_old');

            foreach ([
                'managed_environment_onboarding_sessions_workspace_id_environment_id_unique',
                'managed_environment_onboarding_sessions_environment_id_index',
                'managed_environment_onboarding_sessions_workspace_id_managed_environment_id_unique',
                'managed_environment_onboarding_sessions_managed_environment_id_index',
            ] as $indexName) {
                DB::statement("DROP INDEX IF EXISTS {$indexName}");
            }

            Schema::create('managed_environment_onboarding_sessions', function (Blueprint $table) {
                $table->id();
                $table->foreignId('workspace_id')->constrained()->cascadeOnDelete();
                $table->foreignId('managed_environment_id')->nullable()->constrained()->cascadeOnDelete();
                $table->string('entra_tenant_id');
                $table->string('current_step')->nullable();
                $table->json('state')->nullable();
                $table->foreignId('started_by_user_id')->nullable()->constrained('users')->nullOnDelete();
                $table->foreignId('updated_by_user_id')->nullable()->constrained('users')->nullOnDelete();
                $table->timestamp('completed_at')->nullable();
                $table->timestamps();

                $table->index('managed_environment_id');
                $table->index('entra_tenant_id');
            });

            DB::table('managed_environment_onboarding_sessions_old')
                ->orderBy('id')
                ->chunkById(500, function ($rows): void {
                    foreach ($rows as $row) {
                        $state = is_string($row->state) ? json_decode($row->state, true) : null;
                        $state = is_array($state) ? $state : [];

                        $entraTenantId = $row->entra_tenant_id ?? null;

                        if (! is_string($entraTenantId) || trim($entraTenantId) === '') {
                            $entraTenantId = $state['entra_tenant_id'] ?? $state['managed_environment_id'] ?? null;
                        }

                        if (! is_string($entraTenantId) || trim($entraTenantId) === '') {
                            $entraTenantId = DB::table('managed_environments')
                                ->where('id', $row->managed_environment_id)
                                ->value('slug');
                        }

                        $entraTenantId = is_string($entraTenantId) ? trim($entraTenantId) : '';

                        if ($entraTenantId === '') {
                            $entraTenantId = sprintf('unknown-%d', (int) $row->id);
                        }

                        DB::table('managed_environment_onboarding_sessions')->insert([
                            'id' => $row->id,
                            'workspace_id' => $row->workspace_id,
                            'managed_environment_id' => $row->managed_environment_id,
                            'entra_tenant_id' => $entraTenantId,
                            'current_step' => $row->current_step,
                            'state' => $row->state,
                            'started_by_user_id' => $row->started_by_user_id,
                            'updated_by_user_id' => $row->updated_by_user_id,
                            'completed_at' => $row->completed_at,
                            'created_at' => $row->created_at,
                            'updated_at' => $row->updated_at,
                        ]);
                    }
                }, 'id');

            Schema::drop('managed_environment_onboarding_sessions_old');

            DB::statement('CREATE UNIQUE INDEX managed_environment_onboarding_sessions_active_workspace_entra_unique ON managed_environment_onboarding_sessions (workspace_id, entra_tenant_id) WHERE completed_at IS NULL');
            DB::statement('CREATE UNIQUE INDEX managed_environment_onboarding_sessions_active_environment_unique ON managed_environment_onboarding_sessions (managed_environment_id) WHERE completed_at IS NULL AND managed_environment_id IS NOT NULL');

            Schema::enableForeignKeyConstraints();

            return;
        }

        if (! Schema::hasColumn('managed_environment_onboarding_sessions', 'entra_tenant_id')) {
            Schema::table('managed_environment_onboarding_sessions', function (Blueprint $table) {
                $table->string('entra_tenant_id')->nullable()->after('managed_environment_id');
            });
        }

        $this->backfillEntraTenantId($driver);

        if ($driver === 'pgsql') {
            DB::statement('ALTER TABLE managed_environment_onboarding_sessions ALTER COLUMN managed_environment_id DROP NOT NULL');
            DB::statement('ALTER TABLE managed_environment_onboarding_sessions ALTER COLUMN entra_tenant_id SET NOT NULL');
        }

        Schema::table('managed_environment_onboarding_sessions', function (Blueprint $table) {
            $table->dropUnique(['workspace_id', 'managed_environment_id']);
            $table->index('entra_tenant_id');
        });

        DB::statement('CREATE UNIQUE INDEX IF NOT EXISTS managed_environment_onboarding_sessions_active_workspace_entra_unique ON managed_environment_onboarding_sessions (workspace_id, entra_tenant_id) WHERE completed_at IS NULL');
        DB::statement('CREATE UNIQUE INDEX IF NOT EXISTS managed_environment_onboarding_sessions_active_environment_unique ON managed_environment_onboarding_sessions (managed_environment_id) WHERE completed_at IS NULL AND managed_environment_id IS NOT NULL');
    }

    public function down(): void
    {
        if (! Schema::hasTable('managed_environment_onboarding_sessions')) {
            return;
        }

        $driver = Schema::getConnection()->getDriverName();

        if ($driver === 'sqlite') {
            Schema::disableForeignKeyConstraints();

            Schema::rename('managed_environment_onboarding_sessions', 'managed_environment_onboarding_sessions_new');

            foreach ([
                'managed_environment_onboarding_sessions_active_workspace_entra_unique',
                'managed_environment_onboarding_sessions_active_environment_unique',
                'managed_environment_onboarding_sessions_environment_id_index',
                'managed_environment_onboarding_sessions_managed_environment_id_index',
                'managed_environment_onboarding_sessions_entra_tenant_id_index',
            ] as $indexName) {
                DB::statement("DROP INDEX IF EXISTS {$indexName}");
            }

            Schema::create('managed_environment_onboarding_sessions', function (Blueprint $table) {
                $table->id();
                $table->foreignId('workspace_id')->constrained()->cascadeOnDelete();
                $table->foreignId('managed_environment_id')->constrained()->cascadeOnDelete();
                $table->string('current_step')->nullable();
                $table->json('state')->nullable();
                $table->foreignId('started_by_user_id')->nullable()->constrained('users')->nullOnDelete();
                $table->foreignId('updated_by_user_id')->nullable()->constrained('users')->nullOnDelete();
                $table->timestamp('completed_at')->nullable();
                $table->timestamps();

                $table->unique(['workspace_id', 'managed_environment_id']);
                $table->index(['managed_environment_id']);
            });

            DB::table('managed_environment_onboarding_sessions_new')
                ->whereNotNull('managed_environment_id')
                ->orderBy('id')
                ->chunkById(500, function ($rows): void {
                    foreach ($rows as $row) {
                        DB::table('managed_environment_onboarding_sessions')->insert([
                            'id' => $row->id,
                            'workspace_id' => $row->workspace_id,
                            'managed_environment_id' => $row->managed_environment_id,
                            'current_step' => $row->current_step,
                            'state' => $row->state,
                            'started_by_user_id' => $row->started_by_user_id,
                            'updated_by_user_id' => $row->updated_by_user_id,
                            'completed_at' => $row->completed_at,
                            'created_at' => $row->created_at,
                            'updated_at' => $row->updated_at,
                        ]);
                    }
                }, 'id');

            Schema::drop('managed_environment_onboarding_sessions_new');
            Schema::enableForeignKeyConstraints();

            return;
        }

        foreach ([
            'managed_environment_onboarding_sessions_active_workspace_entra_unique',
            'managed_environment_onboarding_sessions_active_environment_unique',
        ] as $indexName) {
            DB::statement("DROP INDEX IF EXISTS {$indexName}");
        }

        if (Schema::hasColumn('managed_environment_onboarding_sessions', 'entra_tenant_id')) {
            Schema::table('managed_environment_onboarding_sessions', function (Blueprint $table) {
                $table->dropIndex(['entra_tenant_id']);
                $table->dropColumn('entra_tenant_id');
                $table->unique(['workspace_id', 'managed_environment_id']);
            });
        }
    }

    private function backfillEntraTenantId(string $driver): void
    {
        if ($driver === 'pgsql') {
            DB::statement(<<<'SQL'
                UPDATE managed_environment_onboarding_sessions
                SET entra_tenant_id = COALESCE(managed_environment_onboarding_sessions.entra_tenant_id, managed_environment_onboarding_sessions.state->>'entra_tenant_id', managed_environment_onboarding_sessions.state->>'managed_environment_id', managed_environments.slug)
                FROM managed_environments
                WHERE managed_environment_onboarding_sessions.entra_tenant_id IS NULL
                  AND managed_environment_onboarding_sessions.managed_environment_id = managed_environments.id
            SQL);

            DB::statement(<<<'SQL'
                UPDATE managed_environment_onboarding_sessions
                SET entra_tenant_id = state->>'managed_environment_id'
                WHERE entra_tenant_id IS NULL
            SQL);

            return;
        }

        DB::table('managed_environment_onboarding_sessions')
            ->whereNull('entra_tenant_id')
            ->orderBy('id')
            ->chunkById(500, function ($rows): void {
                foreach ($rows as $row) {
                    $state = is_string($row->state) ? json_decode($row->state, true) : null;
                    $state = is_array($state) ? $state : [];

                    $entraTenantId = $state['entra_tenant_id'] ?? $state['managed_environment_id'] ?? null;

                        if (! is_string($entraTenantId) || trim($entraTenantId) === '') {
                            $entraTenantId = DB::table('managed_environments')
                                ->where('id', $row->managed_environment_id)
                                ->value('slug');
                        }

                    $entraTenantId = is_string($entraTenantId) ? trim($entraTenantId) : '';

                    if ($entraTenantId === '') {
                        $entraTenantId = sprintf('unknown-%d', (int) $row->id);
                    }

                    DB::table('managed_environment_onboarding_sessions')
                        ->where('id', $row->id)
                        ->update(['entra_tenant_id' => $entraTenantId]);
                }
            }, 'id');
    }
};
