<?php

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

return new class extends Migration
{
    public function up(): void
    {
        $this->createTables();
        $this->seedInitialData();
        $this->migrateLegacyAssignments();
        $this->dropLegacyTables();
    }

    public function down(): void
    {
        $this->recreateLegacyTables();
        $this->migrateBackToLegacyTables();

        Schema::dropIfExists('admin_user_site_roles');
        Schema::dropIfExists('admin_user_menu_actions');
        Schema::dropIfExists('admin_user_data_scopes');
        Schema::dropIfExists('admin_role_menu_actions');
        Schema::dropIfExists('admin_role_api_resources');
        Schema::dropIfExists('admin_role_menus');
        Schema::dropIfExists('admin_role_data_scopes');
        Schema::dropIfExists('admin_api_resource_bindings');
        Schema::dropIfExists('admin_api_resources');
        Schema::dropIfExists('admin_menu_actions');
        Schema::dropIfExists('admin_action_catalog');
        Schema::dropIfExists('admin_menus');
        Schema::dropIfExists('admin_data_scopes');
        Schema::dropIfExists('admin_sites');
    }

    private function createTables(): void
    {
        Schema::create('admin_sites', function (Blueprint $table): void {
            $table->id();
            $table->string('code', 64)->unique();
            $table->string('name', 128);
            $table->string('currency_code', 16)->default('NPR');
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled');
            $table->boolean('is_default')->default(false);
            $table->json('extra_json')->nullable();
            $table->timestamps();
        });

        Schema::table('admin_roles', function (Blueprint $table): void {
            $table->string('code', 64)->nullable()->after('id');
            $table->text('description')->nullable()->after('name');
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled')->after('description');
            $table->boolean('is_system')->default(false)->after('status');
            $table->unsignedInteger('sort_order')->default(0)->after('is_system');
        });

        DB::table('admin_roles')->update([
            'code' => DB::raw('slug'),
            'status' => 1,
            'is_system' => true,
            'sort_order' => 0,
        ]);

        Schema::table('admin_roles', function (Blueprint $table): void {
            $table->string('code', 64)->nullable(false)->change();
            $table->unique('code');
        });

        Schema::create('admin_menus', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('parent_id')->nullable()->constrained('admin_menus')->nullOnDelete();
            $table->string('menu_type', 24)->comment('directory|menu|page');
            $table->string('code', 128)->unique();
            $table->string('name', 128);
            $table->string('path', 255)->nullable();
            $table->string('route_name', 255)->nullable();
            $table->string('component', 255)->nullable();
            $table->string('icon', 128)->nullable();
            $table->string('active_menu_code', 128)->nullable();
            $table->unsignedInteger('sort_order')->default(0);
            $table->boolean('is_visible')->default(true);
            $table->boolean('is_cache')->default(false);
            $table->boolean('is_external')->default(false);
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled');
            $table->json('meta_json')->nullable();
            $table->timestamps();

            $table->index(['parent_id', 'sort_order'], 'idx_admin_menus_parent_sort');
        });

        Schema::create('admin_action_catalog', function (Blueprint $table): void {
            $table->id();
            $table->string('code', 64)->unique();
            $table->string('name', 64);
            $table->unsignedInteger('sort_order')->default(0);
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled');
            $table->timestamps();
        });

        Schema::create('admin_menu_actions', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('menu_id')->constrained('admin_menus')->cascadeOnDelete();
            $table->foreignId('action_id')->constrained('admin_action_catalog')->cascadeOnDelete();
            $table->string('permission_code', 128)->unique();
            $table->string('name', 128);
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled');
            $table->timestamps();

            $table->unique(['menu_id', 'action_id'], 'uk_admin_menu_actions_menu_action');
            $table->index(['menu_id', 'status'], 'idx_admin_menu_actions_menu_status');
        });

        Schema::create('admin_api_resources', function (Blueprint $table): void {
            $table->id();
            $table->string('code', 128)->unique();
            $table->string('module_code', 64);
            $table->string('name', 128);
            $table->string('http_method', 16);
            $table->string('uri_pattern', 255);
            $table->string('route_name', 255)->nullable();
            $table->string('auth_mode', 24)->default('permission_required')->comment('login_only|permission_required|internal_only');
            $table->boolean('is_audit_required')->default(false);
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled');
            $table->json('meta_json')->nullable();
            $table->timestamps();

            $table->index(['module_code', 'status'], 'idx_admin_api_resources_module_status');
        });

        Schema::create('admin_api_resource_bindings', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('api_resource_id')->constrained('admin_api_resources')->cascadeOnDelete();
            $table->foreignId('menu_action_id')->constrained('admin_menu_actions')->cascadeOnDelete();
            $table->timestamps();

            $table->unique(['api_resource_id', 'menu_action_id'], 'uk_admin_api_bindings_api_action');
        });

        Schema::create('admin_data_scopes', function (Blueprint $table): void {
            $table->id();
            $table->string('code', 64)->unique();
            $table->string('name', 128);
            $table->string('scope_type', 32)->comment('all_sites|site_only|site_all_data|site_single_player|self_only');
            $table->string('module_code', 64)->nullable();
            $table->text('description')->nullable();
            $table->unsignedTinyInteger('status')->default(1)->comment('1=enabled,0=disabled');
            $table->timestamps();
        });

        Schema::create('admin_role_menus', function (Blueprint $table): void {
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->foreignId('menu_id')->constrained('admin_menus')->cascadeOnDelete();
            $table->primary(['role_id', 'menu_id']);
        });

        Schema::create('admin_role_menu_actions', function (Blueprint $table): void {
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->foreignId('menu_action_id')->constrained('admin_menu_actions')->cascadeOnDelete();
            $table->primary(['role_id', 'menu_action_id']);
        });

        Schema::create('admin_role_api_resources', function (Blueprint $table): void {
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->foreignId('api_resource_id')->constrained('admin_api_resources')->cascadeOnDelete();
            $table->primary(['role_id', 'api_resource_id']);
        });

        Schema::create('admin_role_data_scopes', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->foreignId('site_id')->nullable()->constrained('admin_sites')->nullOnDelete();
            $table->foreignId('data_scope_id')->constrained('admin_data_scopes')->cascadeOnDelete();
            $table->string('module_code', 64)->nullable();
            $table->json('constraint_json')->nullable();
            $table->timestamps();

            $table->unique(['role_id', 'site_id', 'data_scope_id', 'module_code'], 'uk_admin_role_data_scopes');
        });

        Schema::create('admin_user_site_roles', function (Blueprint $table): void {
            $table->foreignId('admin_user_id')->constrained('admin_users')->cascadeOnDelete();
            $table->foreignId('site_id')->constrained('admin_sites')->cascadeOnDelete();
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->timestamp('granted_at')->nullable();
            $table->primary(['admin_user_id', 'site_id', 'role_id'], 'pk_admin_user_site_roles');
        });

        Schema::create('admin_user_menu_actions', function (Blueprint $table): void {
            $table->foreignId('admin_user_id')->constrained('admin_users')->cascadeOnDelete();
            $table->foreignId('site_id')->nullable()->constrained('admin_sites')->nullOnDelete();
            $table->foreignId('menu_action_id')->constrained('admin_menu_actions')->cascadeOnDelete();
            $table->timestamp('granted_at')->nullable();
            $table->primary(['admin_user_id', 'site_id', 'menu_action_id'], 'pk_admin_user_menu_actions');
        });

        Schema::create('admin_user_data_scopes', function (Blueprint $table): void {
            $table->id();
            $table->foreignId('admin_user_id')->constrained('admin_users')->cascadeOnDelete();
            $table->foreignId('site_id')->nullable()->constrained('admin_sites')->nullOnDelete();
            $table->foreignId('data_scope_id')->constrained('admin_data_scopes')->cascadeOnDelete();
            $table->string('module_code', 64)->nullable();
            $table->json('constraint_json')->nullable();
            $table->timestamps();

            $table->unique(['admin_user_id', 'site_id', 'data_scope_id', 'module_code'], 'uk_admin_user_data_scopes');
        });
    }

    private function seedInitialData(): void
    {
        $now = Carbon::now();

        DB::table('admin_sites')->insert([
            'code' => 'default_site',
            'name' => '默认站点',
            'currency_code' => 'NPR',
            'status' => 1,
            'is_default' => true,
            'extra_json' => json_encode(['source' => 'migration'], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
            'created_at' => $now,
            'updated_at' => $now,
        ]);

        DB::table('admin_action_catalog')->insert([
            ['code' => 'view', 'name' => '查看', 'sort_order' => 10, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'create', 'name' => '新增', 'sort_order' => 20, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'update', 'name' => '编辑', 'sort_order' => 30, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'delete', 'name' => '删除', 'sort_order' => 40, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'review', 'name' => '审核', 'sort_order' => 50, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'publish', 'name' => '发布', 'sort_order' => 60, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'export', 'name' => '导出', 'sort_order' => 70, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'manage', 'name' => '管理', 'sort_order' => 80, 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
        ]);

        DB::table('admin_data_scopes')->insert([
            ['code' => 'all_sites', 'name' => '全站点', 'scope_type' => 'all_sites', 'module_code' => null, 'description' => '可访问所有站点数据', 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'site_only', 'name' => '指定站点', 'scope_type' => 'site_only', 'module_code' => null, 'description' => '仅限授权站点登录和访问', 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'site_all_data', 'name' => '站点内全部数据', 'scope_type' => 'site_all_data', 'module_code' => null, 'description' => '可访问站点内全部业务数据', 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'site_single_player', 'name' => '站点内单玩家', 'scope_type' => 'site_single_player', 'module_code' => 'player_service', 'description' => '仅限按指定玩家处理客诉与查单', 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
            ['code' => 'self_only', 'name' => '仅本人相关', 'scope_type' => 'self_only', 'module_code' => 'audit', 'description' => '仅可查看与自身相关的数据', 'status' => 1, 'created_at' => $now, 'updated_at' => $now],
        ]);

        $this->seedMenuTree($now);
        $this->seedApiResources($now);
    }

    private function seedMenuTree(Carbon $now): void
    {
        $menus = [
            ['parent_code' => null, 'menu_type' => 'menu', 'code' => 'dashboard', 'name' => '仪表盘', 'path' => '/admin', 'route_name' => 'admin.dashboard', 'component' => 'dashboard/index', 'icon' => 'layout-dashboard', 'sort_order' => 10],
            ['parent_code' => null, 'menu_type' => 'directory', 'code' => 'draw', 'name' => '开奖管理', 'path' => '/admin/draws', 'route_name' => null, 'component' => null, 'icon' => 'dice-5', 'sort_order' => 20],
            ['parent_code' => 'draw', 'menu_type' => 'page', 'code' => 'draw.results', 'name' => '开奖结果', 'path' => '/admin/draws', 'route_name' => 'admin.draws.index', 'component' => 'draw/results', 'icon' => null, 'sort_order' => 10],
            ['parent_code' => 'draw', 'menu_type' => 'page', 'code' => 'draw.review', 'name' => '开奖审核', 'path' => '/admin/draws/review', 'route_name' => 'admin.draws.review', 'component' => 'draw/review', 'icon' => null, 'sort_order' => 20],
            ['parent_code' => null, 'menu_type' => 'directory', 'code' => 'config', 'name' => '运营配置', 'path' => '/admin/config', 'route_name' => null, 'component' => null, 'icon' => 'sliders-horizontal', 'sort_order' => 30],
            ['parent_code' => 'config', 'menu_type' => 'page', 'code' => 'config.play', 'name' => '玩法开关', 'path' => '/admin/config/play-switches', 'route_name' => 'admin.config.play', 'component' => 'config/play', 'icon' => null, 'sort_order' => 10],
            ['parent_code' => 'config', 'menu_type' => 'page', 'code' => 'config.odds', 'name' => '赔率配置', 'path' => '/admin/config/odds', 'route_name' => 'admin.config.odds', 'component' => 'config/odds', 'icon' => null, 'sort_order' => 20],
            ['parent_code' => 'config', 'menu_type' => 'page', 'code' => 'config.risk_cap', 'name' => '封顶配置', 'path' => '/admin/config/play-limits', 'route_name' => 'admin.config.risk_cap', 'component' => 'config/risk-cap', 'icon' => null, 'sort_order' => 30],
            ['parent_code' => 'config', 'menu_type' => 'page', 'code' => 'config.jackpot', 'name' => 'Jackpot 配置', 'path' => '/admin/jackpot/pools', 'route_name' => 'admin.jackpot.pools', 'component' => 'config/jackpot', 'icon' => null, 'sort_order' => 40],
            ['parent_code' => null, 'menu_type' => 'page', 'code' => 'risk.monitor', 'name' => '风控监控', 'path' => '/admin/risk', 'route_name' => 'admin.risk.monitor', 'component' => 'risk/monitor', 'icon' => 'shield-alert', 'sort_order' => 40],
            ['parent_code' => null, 'menu_type' => 'page', 'code' => 'settlement.batch', 'name' => '结算批次', 'path' => '/admin/settlement-batches', 'route_name' => 'admin.settlement.batches', 'component' => 'settlement/batches', 'icon' => 'receipt-text', 'sort_order' => 50],
            ['parent_code' => null, 'menu_type' => 'directory', 'code' => 'service', 'name' => '客服财务', 'path' => '/admin/service-desk', 'route_name' => null, 'component' => null, 'icon' => 'hand-helping', 'sort_order' => 60],
            ['parent_code' => 'service', 'menu_type' => 'page', 'code' => 'service.players', 'name' => '玩家查询', 'path' => '/admin/players', 'route_name' => 'admin.players.index', 'component' => 'service/players', 'icon' => null, 'sort_order' => 10],
            ['parent_code' => 'service', 'menu_type' => 'page', 'code' => 'service.tickets', 'name' => '玩家注单', 'path' => '/admin/tickets', 'route_name' => 'admin.tickets.index', 'component' => 'service/tickets', 'icon' => null, 'sort_order' => 20],
            ['parent_code' => 'service', 'menu_type' => 'page', 'code' => 'service.wallet', 'name' => '钱包流水', 'path' => '/admin/wallet/transactions', 'route_name' => 'admin.wallet.transactions', 'component' => 'service/wallet', 'icon' => null, 'sort_order' => 30],
            ['parent_code' => 'service', 'menu_type' => 'page', 'code' => 'service.reconcile', 'name' => '对账管理', 'path' => '/admin/reconcile', 'route_name' => 'admin.reconcile.index', 'component' => 'service/reconcile', 'icon' => null, 'sort_order' => 40],
            ['parent_code' => 'service', 'menu_type' => 'page', 'code' => 'service.audit', 'name' => '审计日志', 'path' => '/admin/audit-logs', 'route_name' => 'admin.audit.index', 'component' => 'service/audit', 'icon' => null, 'sort_order' => 60],
            ['parent_code' => null, 'menu_type' => 'page', 'code' => 'system.admin_user', 'name' => '管理员权限', 'path' => '/admin/admin-users', 'route_name' => 'admin.system.admin-users', 'component' => 'system/admin-users', 'icon' => 'users-round', 'sort_order' => 70],
            ['parent_code' => null, 'menu_type' => 'page', 'code' => 'system.admin_role', 'name' => '角色管理', 'path' => '/admin/admin-roles', 'route_name' => 'admin.system.admin-roles', 'component' => 'system/admin-roles', 'icon' => 'shield-check', 'sort_order' => 71],
        ];

        $menuIds = [];
        foreach ($menus as $menu) {
            $menuIds[$menu['code']] = DB::table('admin_menus')->insertGetId([
                'parent_id' => $menu['parent_code'] === null ? null : $menuIds[$menu['parent_code']],
                'menu_type' => $menu['menu_type'],
                'code' => $menu['code'],
                'name' => $menu['name'],
                'path' => $menu['path'],
                'route_name' => $menu['route_name'],
                'component' => $menu['component'],
                'icon' => $menu['icon'],
                'active_menu_code' => null,
                'sort_order' => $menu['sort_order'],
                'is_visible' => true,
                'is_cache' => false,
                'is_external' => false,
                'status' => 1,
                'meta_json' => null,
                'created_at' => $now,
                'updated_at' => $now,
            ]);
        }

        $actionIds = DB::table('admin_action_catalog')->pluck('id', 'code');
        $menuActions = [
            ['menu_code' => 'draw.results', 'action_code' => 'view', 'permission_code' => 'draw.results.view', 'name' => '开奖结果查看'],
            ['menu_code' => 'draw.review', 'action_code' => 'review', 'permission_code' => 'draw.review.review', 'name' => '开奖审核'],
            ['menu_code' => 'draw.review', 'action_code' => 'publish', 'permission_code' => 'draw.review.publish', 'name' => '开奖发布'],
            ['menu_code' => 'config.play', 'action_code' => 'manage', 'permission_code' => 'config.play.manage', 'name' => '玩法开关管理'],
            ['menu_code' => 'config.odds', 'action_code' => 'manage', 'permission_code' => 'config.odds.manage', 'name' => '赔率配置管理'],
            ['menu_code' => 'config.risk_cap', 'action_code' => 'view', 'permission_code' => 'config.risk_cap.view', 'name' => '封顶配置查看'],
            ['menu_code' => 'config.risk_cap', 'action_code' => 'manage', 'permission_code' => 'config.risk_cap.manage', 'name' => '封顶配置管理'],
            ['menu_code' => 'config.jackpot', 'action_code' => 'view', 'permission_code' => 'config.jackpot.view', 'name' => 'Jackpot 查看'],
            ['menu_code' => 'config.jackpot', 'action_code' => 'manage', 'permission_code' => 'config.jackpot.manage', 'name' => 'Jackpot 管理'],
            ['menu_code' => 'risk.monitor', 'action_code' => 'view', 'permission_code' => 'risk.monitor.view', 'name' => '风控监控查看'],
            ['menu_code' => 'settlement.batch', 'action_code' => 'view', 'permission_code' => 'settlement.batch.view', 'name' => '结算查看'],
            ['menu_code' => 'settlement.batch', 'action_code' => 'review', 'permission_code' => 'settlement.batch.review', 'name' => '结算审核'],
            ['menu_code' => 'settlement.batch', 'action_code' => 'manage', 'permission_code' => 'settlement.batch.manage', 'name' => '结算执行'],
            ['menu_code' => 'service.players', 'action_code' => 'view', 'permission_code' => 'service.players.view', 'name' => '玩家查询查看'],
            ['menu_code' => 'service.players', 'action_code' => 'manage', 'permission_code' => 'service.players.manage', 'name' => '玩家查询管理'],
            ['menu_code' => 'service.players', 'action_code' => 'update', 'permission_code' => 'service.players.freeze', 'name' => '冻结解冻玩家'],
            ['menu_code' => 'service.tickets', 'action_code' => 'view', 'permission_code' => 'service.tickets.view', 'name' => '玩家注单查看'],
            ['menu_code' => 'service.wallet', 'action_code' => 'view', 'permission_code' => 'service.wallet.view', 'name' => '钱包流水查看'],
            ['menu_code' => 'service.wallet', 'action_code' => 'manage', 'permission_code' => 'service.wallet.manage', 'name' => '钱包流水管理'],
            ['menu_code' => 'service.reconcile', 'action_code' => 'view', 'permission_code' => 'service.reconcile.view', 'name' => '对账查看'],
            ['menu_code' => 'service.reconcile', 'action_code' => 'manage', 'permission_code' => 'service.reconcile.manage', 'name' => '对账管理'],
            ['menu_code' => 'service.audit', 'action_code' => 'view', 'permission_code' => 'service.audit.view', 'name' => '审计查看'],
            ['menu_code' => 'system.admin_user', 'action_code' => 'manage', 'permission_code' => 'system.admin_user.manage', 'name' => '管理员权限管理'],
            ['menu_code' => 'system.admin_role', 'action_code' => 'manage', 'permission_code' => 'system.admin_role.manage', 'name' => '角色权限管理'],
        ];

        foreach ($menuActions as $row) {
            DB::table('admin_menu_actions')->insert([
                'menu_id' => $menuIds[$row['menu_code']],
                'action_id' => $actionIds[$row['action_code']],
                'permission_code' => $row['permission_code'],
                'name' => $row['name'],
                'status' => 1,
                'created_at' => $now,
                'updated_at' => $now,
            ]);
        }
    }

    private function seedApiResources(Carbon $now): void
    {
        $resources = [
            ['code' => 'admin.dashboard', 'module_code' => 'dashboard', 'name' => '后台仪表盘', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/dashboard', 'route_name' => 'api.v1.admin.dashboard', 'auth_mode' => 'login_only', 'is_audit_required' => false, 'permission_codes' => []],
            ['code' => 'admin.draws.index', 'module_code' => 'draw', 'name' => '期开奖列表', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/draws', 'route_name' => 'api.v1.admin.draws.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['draw.results.view']],
            ['code' => 'admin.draws.show', 'module_code' => 'draw', 'name' => '期开奖详情', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/draws/{draw}', 'route_name' => 'api.v1.admin.draws.show', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['draw.results.view']],
            ['code' => 'admin.draws.publish', 'module_code' => 'draw', 'name' => '开奖发布', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/draws/{draw}/result-batches/{batch}/publish', 'route_name' => 'api.v1.admin.draws.result-batches.publish', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['draw.review.publish']],
            ['code' => 'admin.draws.settlement.run', 'module_code' => 'settlement', 'name' => '执行结算', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/draws/{draw}/settlement/run', 'route_name' => 'api.v1.admin.draws.settlement.run', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['settlement.batch.manage', 'settlement.batch.review']],
            ['code' => 'admin.wallet.transactions', 'module_code' => 'wallet', 'name' => '钱包流水查询', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/wallet/transactions', 'route_name' => 'api.v1.admin.wallet.transactions', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['service.wallet.view', 'service.wallet.manage']],
            ['code' => 'admin.wallet.transfer-orders', 'module_code' => 'wallet', 'name' => '转账单查询', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/wallet/transfer-orders', 'route_name' => 'api.v1.admin.wallet.transfer-orders', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['service.wallet.view', 'service.wallet.manage']],
            ['code' => 'admin.players.wallets', 'module_code' => 'player_service', 'name' => '玩家钱包查看', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/players/{player}/wallets', 'route_name' => 'api.v1.admin.players.wallets', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['service.players.manage', 'service.wallet.view']],
            ['code' => 'admin.players.ticket-items', 'module_code' => 'player_service', 'name' => '玩家注单查看', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/players/{player}/ticket-items', 'route_name' => 'api.v1.admin.players.ticket-items.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['service.players.manage', 'service.tickets.view']],
            ['code' => 'admin.reconcile.index', 'module_code' => 'reconcile', 'name' => '对账任务列表', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/reconcile-jobs', 'route_name' => 'api.v1.admin.reconcile-jobs.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['service.reconcile.view', 'service.reconcile.manage']],
            ['code' => 'admin.reconcile.store', 'module_code' => 'reconcile', 'name' => '创建对账任务', 'http_method' => 'POST', 'uri_pattern' => '/api/v1/admin/reconcile-jobs', 'route_name' => 'api.v1.admin.reconcile-jobs.store', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['service.reconcile.manage']],
            ['code' => 'admin.audit.index', 'module_code' => 'audit', 'name' => '审计日志查询', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/audit-logs', 'route_name' => 'api.v1.admin.audit-logs.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['service.audit.view']],
            ['code' => 'admin.admin-users.index', 'module_code' => 'system', 'name' => '管理员列表', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/admin-users', 'route_name' => 'api.v1.admin.admin-users.index', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['system.admin_user.manage']],
            ['code' => 'admin.admin-users.permission-catalog', 'module_code' => 'system', 'name' => '管理员权限目录', 'http_method' => 'GET', 'uri_pattern' => '/api/v1/admin/admin-user-permission-catalog', 'route_name' => 'api.v1.admin.admin-users.permission-catalog', 'auth_mode' => 'permission_required', 'is_audit_required' => false, 'permission_codes' => ['system.admin_user.manage']],
            ['code' => 'admin.admin-users.permissions.sync', 'module_code' => 'system', 'name' => '管理员权限同步', 'http_method' => 'PUT', 'uri_pattern' => '/api/v1/admin/admin-users/{admin_user}/permissions', 'route_name' => 'api.v1.admin.admin-users.permissions.sync', 'auth_mode' => 'permission_required', 'is_audit_required' => true, 'permission_codes' => ['system.admin_user.manage']],
        ];

        $menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
        foreach ($resources as $resource) {
            $resourceId = DB::table('admin_api_resources')->insertGetId([
                'code' => $resource['code'],
                'module_code' => $resource['module_code'],
                'name' => $resource['name'],
                'http_method' => $resource['http_method'],
                'uri_pattern' => $resource['uri_pattern'],
                'route_name' => $resource['route_name'],
                'auth_mode' => $resource['auth_mode'],
                'is_audit_required' => $resource['is_audit_required'],
                'status' => 1,
                'meta_json' => null,
                'created_at' => $now,
                'updated_at' => $now,
            ]);

            foreach ($resource['permission_codes'] as $permissionCode) {
                if (! isset($menuActionIds[$permissionCode])) {
                    continue;
                }
                DB::table('admin_api_resource_bindings')->insert([
                    'api_resource_id' => $resourceId,
                    'menu_action_id' => $menuActionIds[$permissionCode],
                    'created_at' => $now,
                    'updated_at' => $now,
                ]);
            }
        }
    }

    private function migrateLegacyAssignments(): void
    {
        $now = Carbon::now();
        $defaultSiteId = (int) DB::table('admin_sites')->where('is_default', true)->value('id');

        $legacyRoles = DB::table('admin_roles')
            ->select('id', 'code', 'slug', 'name')
            ->get();

        $legacyRoleAssignments = DB::table('admin_user_roles')->get();
        foreach ($legacyRoleAssignments as $row) {
            $legacyRoleId = (int) $row->role_id;
            DB::table('admin_user_site_roles')->insert([
                'admin_user_id' => (int) $row->admin_user_id,
                'site_id' => $defaultSiteId,
                'role_id' => $legacyRoleId,
                'granted_at' => $now,
            ]);
        }

        $legacyPermissionById = DB::table('admin_permissions')->pluck('slug', 'id');
        $legacyRolePermissions = DB::table('admin_role_permissions')->get()->groupBy('role_id');
        $legacyUserPermissions = DB::table('admin_user_permissions')->get()->groupBy('admin_user_id');
        $menuActionIds = DB::table('admin_menu_actions')->pluck('id', 'permission_code');
        $apiResourceIdsByPermission = DB::table('admin_api_resource_bindings')
            ->join('admin_menu_actions', 'admin_menu_actions.id', '=', 'admin_api_resource_bindings.menu_action_id')
            ->select('admin_menu_actions.permission_code', 'admin_api_resource_bindings.api_resource_id')
            ->get()
            ->groupBy('permission_code')
            ->map(static fn ($rows) => $rows->pluck('api_resource_id')->all());
        $menuIdsByPermission = DB::table('admin_menu_actions')
            ->join('admin_menus', 'admin_menus.id', '=', 'admin_menu_actions.menu_id')
            ->pluck('admin_menus.id', 'admin_menu_actions.permission_code');

        $legacyToNewPermissionMap = [
            'prd.users.manage' => ['service.players.manage'],
            'prd.users.view_finance' => ['service.players.view', 'service.wallet.view'],
            'prd.users.view_cs' => ['service.players.view', 'service.tickets.view'],
            'prd.play_switch.manage' => ['config.play.manage'],
            'prd.odds.manage' => ['config.odds.manage'],
            'prd.risk_cap.manage' => ['config.risk_cap.manage'],
            'prd.risk_cap.view' => ['config.risk_cap.view'],
            'prd.rebate.manage' => ['config.odds.manage'],
            'prd.rebate.view' => ['config.odds.manage'],
            'prd.jackpot.manage' => ['config.jackpot.manage'],
            'prd.jackpot.view' => ['config.jackpot.view'],
            'prd.draw_result.manage' => ['draw.results.view', 'draw.review.review', 'draw.review.publish', 'risk.monitor.view'],
            'prd.draw_result.view' => ['draw.results.view', 'risk.monitor.view'],
            'prd.payout.manage' => ['settlement.batch.manage', 'settlement.batch.view'],
            'prd.payout.review' => ['settlement.batch.review', 'settlement.batch.view'],
            'prd.payout.view' => ['settlement.batch.view'],
            'prd.wallet_reconcile.manage' => ['service.wallet.manage', 'service.reconcile.manage'],
            'prd.wallet_reconcile.view' => ['service.wallet.view', 'service.reconcile.view'],
            'prd.wallet_reconcile.view_cs' => ['service.wallet.view', 'service.reconcile.view'],
            'prd.audit.all' => ['service.audit.view'],
            'prd.audit.self' => ['service.audit.view'],
            'prd.audit.finance' => ['service.audit.view'],
            'prd.admin_user.manage' => ['system.admin_user.manage'],
            'prd.player_freeze.manage' => ['service.players.freeze'],
            'prd.wallet_adjust.manage' => ['service.wallet.manage'],
            'prd.draw_reopen.manage' => ['draw.review.publish'],
        ];

        foreach ($legacyRoles as $role) {
            $roleId = (int) $role->id;

            $grantedPermissions = [];
            foreach ($legacyRolePermissions->get((int) $role->id, collect()) as $pivot) {
                $permissionId = (int) $pivot->permission_id;
                $legacySlug = $legacyPermissionById[$permissionId] ?? null;
                if (! is_string($legacySlug)) {
                    continue;
                }
                foreach ($legacyToNewPermissionMap[$legacySlug] ?? [] as $permissionCode) {
                    $grantedPermissions[$permissionCode] = true;
                }
            }

            foreach (array_keys($grantedPermissions) as $permissionCode) {
                if (isset($menuIdsByPermission[$permissionCode])) {
                    $this->grantMenuWithAncestors($roleId, (int) $menuIdsByPermission[$permissionCode]);
                }
                if (isset($menuActionIds[$permissionCode])) {
                    DB::table('admin_role_menu_actions')->updateOrInsert([
                        'role_id' => $roleId,
                        'menu_action_id' => (int) $menuActionIds[$permissionCode],
                    ]);
                }
                foreach ($apiResourceIdsByPermission[$permissionCode] ?? [] as $apiResourceId) {
                    DB::table('admin_role_api_resources')->updateOrInsert([
                        'role_id' => $roleId,
                        'api_resource_id' => (int) $apiResourceId,
                    ]);
                }
            }

            $roleCode = (string) ($role->code ?: $role->slug);
            $this->assignRoleDataScopes($roleId, $roleCode, $defaultSiteId, $now);
        }

        foreach ($legacyUserPermissions as $adminUserId => $pivots) {
            $grantedPermissions = [];
            foreach ($pivots as $pivot) {
                $permissionId = (int) $pivot->permission_id;
                $legacySlug = $legacyPermissionById[$permissionId] ?? null;
                if (! is_string($legacySlug)) {
                    continue;
                }
                foreach ($legacyToNewPermissionMap[$legacySlug] ?? [] as $permissionCode) {
                    $grantedPermissions[$permissionCode] = true;
                }
            }

            foreach (array_keys($grantedPermissions) as $permissionCode) {
                if (! isset($menuActionIds[$permissionCode])) {
                    continue;
                }
                DB::table('admin_user_menu_actions')->updateOrInsert([
                    'admin_user_id' => (int) $adminUserId,
                    'site_id' => $defaultSiteId,
                    'menu_action_id' => (int) $menuActionIds[$permissionCode],
                ], [
                    'granted_at' => $now,
                ]);
            }
        }
    }

    private function assignRoleDataScopes(int $roleId, string $roleCode, int $siteId, Carbon $now): void
    {
        $dataScopeIds = DB::table('admin_data_scopes')->pluck('id', 'code');

        $rows = match ($roleCode) {
            'super_admin' => [
                ['scope_code' => 'all_sites', 'module_code' => null],
                ['scope_code' => 'site_all_data', 'module_code' => null],
            ],
            'risk_operator' => [
                ['scope_code' => 'site_only', 'module_code' => null],
                ['scope_code' => 'site_all_data', 'module_code' => 'risk'],
                ['scope_code' => 'self_only', 'module_code' => 'audit'],
            ],
            'finance' => [
                ['scope_code' => 'site_only', 'module_code' => null],
                ['scope_code' => 'site_all_data', 'module_code' => 'wallet'],
                ['scope_code' => 'site_all_data', 'module_code' => 'settlement'],
                ['scope_code' => 'site_all_data', 'module_code' => 'report'],
                ['scope_code' => 'site_all_data', 'module_code' => 'reconcile'],
            ],
            'customer_service' => [
                ['scope_code' => 'site_only', 'module_code' => null],
                ['scope_code' => 'site_single_player', 'module_code' => 'player_service'],
            ],
            default => [
                ['scope_code' => 'site_only', 'module_code' => null],
            ],
        };

        foreach ($rows as $row) {
            $scopeId = $dataScopeIds[$row['scope_code']] ?? null;
            if ($scopeId === null) {
                continue;
            }
            DB::table('admin_role_data_scopes')->insert([
                'role_id' => $roleId,
                'site_id' => $row['scope_code'] === 'all_sites' ? null : $siteId,
                'data_scope_id' => (int) $scopeId,
                'module_code' => $row['module_code'],
                'constraint_json' => null,
                'created_at' => $now,
                'updated_at' => $now,
            ]);
        }
    }

    private function grantMenuWithAncestors(int $roleId, int $menuId): void
    {
        $currentMenuId = $menuId;

        while ($currentMenuId > 0) {
            DB::table('admin_role_menus')->updateOrInsert([
                'role_id' => $roleId,
                'menu_id' => $currentMenuId,
            ]);

            $parentId = DB::table('admin_menus')->where('id', $currentMenuId)->value('parent_id');
            $currentMenuId = $parentId === null ? 0 : (int) $parentId;
        }
    }

    private function dropLegacyTables(): void
    {
        Schema::dropIfExists('admin_user_permissions');
        Schema::dropIfExists('admin_user_roles');
        Schema::dropIfExists('admin_role_permissions');
        Schema::dropIfExists('admin_permissions');
    }

    private function recreateLegacyTables(): void
    {
        Schema::table('admin_roles', function (Blueprint $table): void {
            $table->dropUnique(['code']);
            $table->dropColumn(['code', 'description', 'status', 'is_system', 'sort_order']);
        });

        Schema::create('admin_permissions', function (Blueprint $table): void {
            $table->id();
            $table->string('slug', 128)->unique();
            $table->string('name', 128);
            $table->timestamps();
        });

        Schema::create('admin_role_permissions', function (Blueprint $table): void {
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->foreignId('permission_id')->constrained('admin_permissions')->cascadeOnDelete();
            $table->primary(['role_id', 'permission_id']);
        });

        Schema::create('admin_user_roles', function (Blueprint $table): void {
            $table->foreignId('admin_user_id')->constrained('admin_users')->cascadeOnDelete();
            $table->foreignId('role_id')->constrained('admin_roles')->cascadeOnDelete();
            $table->primary(['admin_user_id', 'role_id']);
        });

        Schema::create('admin_user_permissions', function (Blueprint $table): void {
            $table->foreignId('admin_user_id')->constrained('admin_users')->cascadeOnDelete();
            $table->foreignId('permission_id')->constrained('admin_permissions')->cascadeOnDelete();
            $table->primary(['admin_user_id', 'permission_id']);
        });
    }

    private function migrateBackToLegacyTables(): void
    {
        $now = Carbon::now();

        $legacyPermissions = [
            ['slug' => 'prd.users.manage', 'name' => '用户管理·可管理'],
            ['slug' => 'prd.users.view_finance', 'name' => '用户管理·财务查看'],
            ['slug' => 'prd.users.view_cs', 'name' => '用户管理·客服单用户'],
            ['slug' => 'prd.play_switch.manage', 'name' => '玩法开关·可管理'],
            ['slug' => 'prd.odds.manage', 'name' => '赔率配置·可管理'],
            ['slug' => 'prd.risk_cap.manage', 'name' => '封顶配置·可管理'],
            ['slug' => 'prd.risk_cap.view', 'name' => '封顶配置·查看'],
            ['slug' => 'prd.rebate.manage', 'name' => '佣金/回水·可管理'],
            ['slug' => 'prd.rebate.view', 'name' => '佣金/回水·查看'],
            ['slug' => 'prd.jackpot.manage', 'name' => 'Jackpot 配置·可管理'],
            ['slug' => 'prd.jackpot.view', 'name' => 'Jackpot 配置·查看'],
            ['slug' => 'prd.draw_result.manage', 'name' => '开奖结果录入·可管理'],
            ['slug' => 'prd.draw_result.view', 'name' => '开奖结果·查看'],
            ['slug' => 'prd.draw_reopen.manage', 'name' => '开奖结果重开·可管理'],
            ['slug' => 'prd.payout.manage', 'name' => '派彩确认·可管理'],
            ['slug' => 'prd.payout.review', 'name' => '派彩确认·可审核'],
            ['slug' => 'prd.payout.view', 'name' => '派彩确认·查看'],
            ['slug' => 'prd.wallet_reconcile.manage', 'name' => '钱包对账·可管理'],
            ['slug' => 'prd.wallet_reconcile.view', 'name' => '钱包对账·查看'],
            ['slug' => 'prd.wallet_reconcile.view_cs', 'name' => '钱包对账·客服单用户'],
            ['slug' => 'prd.wallet_adjust.manage', 'name' => '补单/冲正·可管理'],
            ['slug' => 'prd.audit.all', 'name' => '审计日志·全部'],
            ['slug' => 'prd.audit.self', 'name' => '审计日志·自身相关'],
            ['slug' => 'prd.audit.finance', 'name' => '审计日志·资金相关'],
            ['slug' => 'prd.player_freeze.manage', 'name' => '冻结/解冻玩家·可管理'],
            ['slug' => 'prd.admin_user.manage', 'name' => '后台用户权限管理·可管理'],
        ];

        foreach ($legacyPermissions as $permission) {
            DB::table('admin_permissions')->insert([
                'slug' => $permission['slug'],
                'name' => $permission['name'],
                'created_at' => $now,
                'updated_at' => $now,
            ]);
        }

        $permissionIds = DB::table('admin_permissions')->pluck('id', 'slug');
        $roleCodeMap = DB::table('admin_roles')->pluck('id', 'slug');

        $rolePermissionMap = [
            'super_admin' => array_keys($permissionIds->all()),
            'risk_operator' => [
                'prd.play_switch.manage',
                'prd.odds.manage',
                'prd.risk_cap.manage',
                'prd.rebate.manage',
                'prd.jackpot.manage',
                'prd.draw_result.manage',
                'prd.payout.review',
                'prd.wallet_reconcile.view',
                'prd.audit.self',
                'prd.player_freeze.manage',
            ],
            'finance' => [
                'prd.users.view_finance',
                'prd.risk_cap.view',
                'prd.rebate.view',
                'prd.jackpot.view',
                'prd.draw_result.view',
                'prd.payout.view',
                'prd.wallet_reconcile.manage',
                'prd.wallet_adjust.manage',
                'prd.audit.finance',
            ],
            'customer_service' => [
                'prd.users.view_cs',
                'prd.draw_result.view',
                'prd.wallet_reconcile.view_cs',
            ],
        ];

        foreach ($rolePermissionMap as $roleCode => $permissionSlugs) {
            $roleId = $roleCodeMap[$roleCode] ?? null;
            if ($roleId === null) {
                continue;
            }

            foreach ($permissionSlugs as $slug) {
                $permissionId = $permissionIds[$slug] ?? null;
                if ($permissionId === null) {
                    continue;
                }
                DB::table('admin_role_permissions')->insert([
                    'role_id' => (int) $roleId,
                    'permission_id' => (int) $permissionId,
                ]);
            }
        }

        $userRoles = DB::table('admin_user_site_roles')
            ->select('admin_user_id', 'role_id')
            ->distinct()
            ->get();

        foreach ($userRoles as $row) {
            DB::table('admin_user_roles')->insert([
                'admin_user_id' => (int) $row->admin_user_id,
                'role_id' => (int) $row->role_id,
            ]);
        }
    }
};
