<?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
    {
        // ===== permissions =====
        if (Schema::hasTable('permissions')) {
            Schema::table('permissions', function (Blueprint $table) {
                if (! Schema::hasColumn('permissions', 'guard_name')) {
                    $table->string('guard_name', 50)->default('api')->after('name');
                }
                if (! Schema::hasColumn('permissions', 'tenant_id')) {
                    $table->unsignedBigInteger('tenant_id')->nullable()->after('guard_name');
                    $table->index(['tenant_id']);
                }
            });

            // unique(name, guard_name, tenant_id)
            // 기존 unique가 name 또는 (name, guard_name) 로 있을 수 있으니 안전하게 제거 후 재생성
            $this->dropUniqueIfExists('permissions', 'permissions_name_unique');
            $this->dropUniqueIfExists('permissions', 'permissions_name_guard_name_unique');
            $this->dropUniqueIfExists('permissions', 'uk_permissions_name_guard_tenant');

            Schema::table('permissions', function (Blueprint $table) {
                $table->unique(['name', 'guard_name', 'tenant_id'], 'uk_permissions_name_guard_tenant');
            });
        }

        // ===== roles =====
        if (Schema::hasTable('roles')) {
            Schema::table('roles', function (Blueprint $table) {
                if (! Schema::hasColumn('roles', 'guard_name')) {
                    $table->string('guard_name', 50)->default('api')->after('name');
                }
                if (! Schema::hasColumn('roles', 'tenant_id')) {
                    // 이미 있으시다 했지만 혹시 없을 경우 대비
                    $table->unsignedBigInteger('tenant_id')->nullable()->after('guard_name');
                }
            });

            // unique(tenant_id, name, guard_name) 로 교체
            $this->dropUniqueIfExists('roles', 'uk_roles_tenant_name');
            $this->dropUniqueIfExists('roles', 'roles_name_unique');
            $this->dropUniqueIfExists('roles', 'roles_name_guard_name_unique');
            $this->dropUniqueIfExists('roles', 'uk_roles_tenant_name_guard');

            Schema::table('roles', function (Blueprint $table) {
                $table->unique(['tenant_id', 'name', 'guard_name'], 'uk_roles_tenant_name_guard');
            });

            // 인덱스 보강
            $this->createIndexIfNotExists('roles', 'roles_tenant_id_index', ['tenant_id']);
        }

        // ===== model_has_roles =====
        if (Schema::hasTable('model_has_roles')) {
            Schema::table('model_has_roles', function (Blueprint $table) {
                if (! Schema::hasColumn('model_has_roles', 'tenant_id')) {
                    $table->unsignedBigInteger('tenant_id')->nullable()->after('model_id');
                }
            });

            // ✅ FK가 role_id를 참조하므로, PK 교체 전에 보조 인덱스 추가
            $this->createIndexIfNotExists('model_has_roles', 'mhr_role_id_idx', ['role_id']);

            // PK: (role_id, model_id, model_type, tenant_id)
            $this->replacePrimaryKey(
                'model_has_roles',
                ['role_id', 'model_id', 'model_type'],
                ['role_id', 'model_id', 'model_type', 'tenant_id']
            );

            // 인덱스 (model_id, model_type, tenant_id)
            $this->dropIndexIfExists('model_has_roles', 'model_has_roles_model_id_model_type_index');
            $this->createIndexIfNotExists(
                'model_has_roles',
                'model_has_roles_model_id_model_type_tenant_id_index',
                ['model_id', 'model_type', 'tenant_id']
            );
        }

        // ===== model_has_permissions =====
        if (Schema::hasTable('model_has_permissions')) {
            Schema::table('model_has_permissions', function (Blueprint $table) {
                if (! Schema::hasColumn('model_has_permissions', 'tenant_id')) {
                    $table->unsignedBigInteger('tenant_id')->nullable()->after('model_id');
                }
            });

            // ✅ FK가 permission_id를 참조하므로, PK 교체 전에 보조 인덱스 추가
            $this->createIndexIfNotExists('model_has_permissions', 'mhp_permission_id_idx', ['permission_id']);

            // PK: (permission_id, model_id, model_type, tenant_id)
            $this->replacePrimaryKey(
                'model_has_permissions',
                ['permission_id', 'model_id', 'model_type'],
                ['permission_id', 'model_id', 'model_type', 'tenant_id']
            );

            // 인덱스 (model_id, model_type, tenant_id)
            $this->dropIndexIfExists('model_has_permissions', 'model_has_permissions_model_id_model_type_index');
            $this->createIndexIfNotExists(
                'model_has_permissions',
                'model_has_permissions_model_id_model_type_tenant_id_index',
                ['model_id', 'model_type', 'tenant_id']
            );
        }
    }

    public function down(): void
    {
        // 되돌리기 (가능한 범위에서)
        if (Schema::hasTable('model_has_permissions')) {
            // PK 원복: (permission_id, model_id, model_type)
            $this->replacePrimaryKey('model_has_permissions', ['permission_id', 'model_id', 'model_type', 'tenant_id'], ['permission_id', 'model_id', 'model_type']);
            $this->dropIndexIfExists('model_has_permissions', 'model_has_permissions_model_id_model_type_tenant_id_index');
            $this->createIndexIfNotExists('model_has_permissions', 'model_has_permissions_model_id_model_type_index', ['model_id', 'model_type']);

            if (Schema::hasColumn('model_has_permissions', 'tenant_id')) {
                Schema::table('model_has_permissions', function (Blueprint $table) {
                    $table->dropColumn('tenant_id');
                });
            }
        }

        if (Schema::hasTable('model_has_roles')) {
            // PK 원복: (role_id, model_id, model_type)
            $this->replacePrimaryKey('model_has_roles', ['role_id', 'model_id', 'model_type', 'tenant_id'], ['role_id', 'model_id', 'model_type']);
            $this->dropIndexIfExists('model_has_roles', 'model_has_roles_model_id_model_type_tenant_id_index');
            $this->createIndexIfNotExists('model_has_roles', 'model_has_roles_model_id_model_type_index', ['model_id', 'model_type']);

            if (Schema::hasColumn('model_has_roles', 'tenant_id')) {
                Schema::table('model_has_roles', function (Blueprint $table) {
                    $table->dropColumn('tenant_id');
                });
            }
        }

        if (Schema::hasTable('roles')) {
            // unique 원복: (tenant_id, name)
            $this->dropUniqueIfExists('roles', 'uk_roles_tenant_name_guard');
            Schema::table('roles', function (Blueprint $table) {
                $table->unique(['tenant_id', 'name'], 'uk_roles_tenant_name');
            });

            if (Schema::hasColumn('roles', 'guard_name')) {
                Schema::table('roles', function (Blueprint $table) {
                    $table->dropColumn('guard_name');
                });
            }
        }

        if (Schema::hasTable('permissions')) {
            // unique 원복: (name) 또는 (name, guard_name) 상황에 따라
            $this->dropUniqueIfExists('permissions', 'uk_permissions_name_guard_tenant');
            Schema::table('permissions', function (Blueprint $table) {
                // 최소 name unique로 복원
                $table->unique(['name'], 'permissions_name_unique');
            });

            if (Schema::hasColumn('permissions', 'tenant_id')) {
                Schema::table('permissions', function (Blueprint $table) {
                    $table->dropColumn('tenant_id');
                });
            }
            if (Schema::hasColumn('permissions', 'guard_name')) {
                Schema::table('permissions', function (Blueprint $table) {
                    $table->dropColumn('guard_name');
                });
            }
        }
    }

    // ---------- helpers ----------

    private function dropUniqueIfExists(string $table, string $index): void
    {
        try {
            Schema::table($table, function (Blueprint $t) use ($index) {
                $t->dropUnique($index);
            });
        } catch (\Throwable $e) {
            // 무시 (없으면 통과)
        }
    }

    private function dropIndexIfExists(string $table, string $index): void
    {
        try {
            Schema::table($table, function (Blueprint $t) use ($index) {
                $t->dropIndex($index);
            });
        } catch (\Throwable $e) {
            // 무시
        }
    }

    private function createIndexIfNotExists(string $table, string $index, array $columns): void
    {
        // 라라벨은 인덱스 존재 체크 API가 없어 try/catch로 처리
        try {
            Schema::table($table, function (Blueprint $t) use ($index, $columns) {
                $t->index($columns, $index);
            });
        } catch (\Throwable $e) {
            // 이미 있으면 통과
        }
    }

    /**
     * 기존 PK를 다른 조합으로 교체
     */
    private function replacePrimaryKey(string $table, array $old, array $new): void
    {
        try {
            Schema::table($table, function (Blueprint $t) use ($old) {
                $t->dropPrimary($this->primaryNameGuess($table, $old));
            });
        } catch (\Throwable $e) {
            // 일부 DB는 이름 지정 안 하면 실패 → 수동 SQL
            $this->dropPrimaryKeyBySql($table);
        }

        Schema::table($table, function (Blueprint $t) use ($new) {
            $t->primary($new);
        });
    }

    private function primaryNameGuess(string $table, array $cols): string
    {
        // 보통 {table}_primary 이지만, 드라이버/버전에 따라 다를 수 있어 try/catch로 대체
        return "{$table}_primary";
    }

    private function dropPrimaryKeyBySql(string $table): void
    {
        $driver = DB::getDriverName();
        if ($driver === 'mysql') {
            DB::statement("ALTER TABLE `{$table}` DROP PRIMARY KEY");
        } elseif ($driver === 'pgsql') {
            // PostgreSQL은 PK 제약명 조회 후 드롭이 필요. 간단히 시도:
            $constraint = DB::selectOne("
                SELECT constraint_name
                FROM information_schema.table_constraints
                WHERE table_name = ? AND constraint_type = 'PRIMARY KEY'
                LIMIT 1
            ", [$table]);
            if ($constraint && isset($constraint->constraint_name)) {
                DB::statement("ALTER TABLE \"{$table}\" DROP CONSTRAINT \"{$constraint->constraint_name}\"");
            }
        } else {
            // 기타 드라이버는 수동 처리 생략
        }
    }
};
