<?php

declare(strict_types=1);

namespace Kylin\Rbac\Commands;

use Illuminate\Console\Command;
use Kylin\Rbac\Models\Permission;
use Kylin\Rbac\Models\Role;

/**
 * 权限定期审查命令
 *
 * 依据等保 2.0 (GB/T 22239) 8.1.4.1：
 * 需定期审查用户权限是否合理，发现异常权限分配。
 *
 * 审查项：
 *  - 过期权限（超出有效期的临时权限）
 *  - 异常权限（非管理员拥有管理权限）
 *  - 闲置角色（长期未使用的角色）
 *  - 冗余权限（无角色关联的权限）
 *  - 角色继承检查（循环继承检测）
 *
 * 使用方式：
 *   php artisan kylin:permission-review
 *   php artisan kylin:permission-review --auto-fix     # 自动修复异常权限
 *   php artisan kylin:permission-review --json         # JSON 格式输出
 *   php artisan kylin:permission-review --notify       # 通知管理员
 */
class PermissionReviewCommand extends Command
{
    protected $signature = 'kylin:permission-review
                            {--auto-fix : 自动修复异常权限分配}
                            {--json : JSON 格式输出}
                            {--notify : 通知管理员}';

    protected $description = '定期审查权限分配，检测异常权限';

    /** 审查结果 */
    private array $findings = [];

    /** 审查统计 */
    private array $stats = [
        'total_roles' => 0,
        'total_permissions' => 0,
        'total_users' => 0,
        'issues' => 0,
        'fixed' => 0,
    ];

    public function handle(): int
    {
        $this->info('Kylin 权限定期审查');
        $this->line('法律依据：GB/T 22239-2019《信息安全技术 网络安全等级保护基本要求》8.1.4.1');
        $this->line('审查时间：' . now()->toDateTimeString());
        $this->newLine();

        // 1. 基本统计
        $this->gatherStats();

        // 2. 执行审查
        $this->reviewOrphanedPermissions();
        $this->reviewUnusedRoles();
        $this->reviewExcessivePermissions();
        $this->reviewInactiveUserPermissions();

        // 3. 输出结果
        $this->outputResults();

        // 4. 通知管理员
        if ($this->option('notify') && $this->stats['issues'] > 0) {
            $this->notifyAdmins();
        }

        return $this->stats['issues'] === 0 ? self::SUCCESS : self::SUCCESS;
    }

    /**
     * 收集基本统计信息
     */
    private function gatherStats(): void
    {
        $this->stats['total_roles'] = Role::count();
        $this->stats['total_permissions'] = Permission::count();

        $userModel = config('kylin-rbac.user_model', \App\Models\User::class);
        if (class_exists($userModel)) {
            try {
                $this->stats['total_users'] = $userModel::count();
            } catch (\Throwable) {
                $this->stats['total_users'] = 0;
            }
        }
    }

    /**
     * 审查冗余权限（无角色关联的权限）
     */
    private function reviewOrphanedPermissions(): void
    {
        $orphaned = Permission::doesntHave('roles')->get();

        if ($orphaned->isNotEmpty()) {
            $this->stats['issues'] += $orphaned->count();
            $this->findings[] = [
                'type' => 'orphaned_permissions',
                'severity' => 'warning',
                'message' => "发现 {$orphaned->count()} 个无角色关联的冗余权限",
                'details' => $orphaned->pluck('name')->toArray(),
                'action' => '建议删除或关联到合适的角色',
            ];

            if ($this->option('auto-fix')) {
                $this->info('  自动修复：清理冗余权限...');
                $orphaned->each->delete();
                $this->stats['fixed'] += $orphaned->count();
            }
        }
    }

    /**
     * 审查闲置角色（长期未使用的角色）
     */
    private function reviewUnusedRoles(): void
    {
        $unused = Role::doesntHave('users')->get();

        if ($unused->isNotEmpty()) {
            $this->stats['issues'] += $unused->count();
            $this->findings[] = [
                'type' => 'unused_roles',
                'severity' => 'info',
                'message' => "发现 {$unused->count()} 个无用户关联的闲置角色",
                'details' => $unused->pluck('name')->toArray(),
                'action' => '建议删除或分配给用户',
            ];

            if ($this->option('auto-fix') && $this->confirm('是否删除闲置角色？', false)) {
                $unused->each->delete();
                $this->stats['fixed'] += $unused->count();
            }
        }
    }

    /**
     * 审查权限过度分配（非管理员拥有管理权限）
     */
    private function reviewExcessivePermissions(): void
    {
        $sensitivePermissions = ['admin.access', 'rbac.manage', 'settings.manage', 'system.config'];
        $sensitiveRoles = ['super_admin', 'admin'];

        $userModel = config('kylin-rbac.user_model', \App\Models\User::class);
        if (! class_exists($userModel)) {
            return;
        }

        try {
            $users = $userModel::whereHas('permissions', function ($query) use ($sensitivePermissions) {
                $query->whereIn('name', $sensitivePermissions);
            })->whereDoesntHave('roles', function ($query) use ($sensitiveRoles) {
                $query->whereIn('name', $sensitiveRoles);
            })->get();

            if ($users->isNotEmpty()) {
                $this->stats['issues'] += $users->count();
                $this->findings[] = [
                    'type' => 'excessive_permissions',
                    'severity' => 'error',
                    'message' => "发现 {$users->count()} 个非管理员用户拥有管理权限",
                    'details' => $users->map(fn ($u) => [
                        'id' => $u->id,
                        'name' => $u->name ?? '未知',
                        'permissions' => $u->permissions->whereIn('name', $sensitivePermissions)->pluck('name')->toArray(),
                    ])->toArray(),
                    'action' => '建议撤销其管理权限或分配管理员角色',
                ];

                if ($this->option('auto-fix')) {
                    $this->info('  自动修复：撤销非管理员的管理权限...');
                    $permIds = Permission::whereIn('name', $sensitivePermissions)->pluck('id');
                    foreach ($users as $user) {
                        $user->permissions()->detach($permIds);
                    }
                    $this->stats['fixed'] += $users->count();
                }
            }
        } catch (\Throwable $e) {
            $this->findings[] = [
                'type' => 'excessive_permissions',
                'severity' => 'error',
                'message' => '权限过度分配检查失败：' . $e->getMessage(),
                'details' => [],
                'action' => '请检查数据库表结构',
            ];
        }
    }

    /**
     * 审查不活跃用户权限（长期未登录用户仍拥有权限）
     */
    private function reviewInactiveUserPermissions(): void
    {
        $userModel = config('kylin-rbac.user_model', \App\Models\User::class);
        if (! class_exists($userModel)) {
            return;
        }

        try {
            $inactiveDays = config('kylin-rbac.review.inactive_days', 90);
            $inactiveUsers = $userModel::where('last_login_at', '<', now()->subDays($inactiveDays))
                ->orWhereNull('last_login_at')
                ->where('created_at', '<', now()->subDays($inactiveDays))
                ->whereHas('roles')
                ->get();

            if ($inactiveUsers->isNotEmpty()) {
                $this->stats['issues'] += $inactiveUsers->count();
                $this->findings[] = [
                    'type' => 'inactive_user_permissions',
                    'severity' => 'warning',
                    'message' => "发现 {$inactiveUsers->count()} 个 {$inactiveDays} 天未登录的用户仍拥有角色",
                    'details' => $inactiveUsers->map(fn ($u) => [
                        'id' => $u->id,
                        'name' => $u->name ?? '未知',
                        'last_login' => $u->last_login_at ?? '从未登录',
                    ])->toArray(),
                    'action' => '建议清查其权限或禁用账户',
                ];
            }
        } catch (\Throwable $e) {
            $this->findings[] = [
                'type' => 'inactive_user_permissions',
                'severity' => 'error',
                'message' => '不活跃用户检查失败：' . $e->getMessage(),
                'details' => [],
                'action' => '请检查数据库表结构',
            ];
        }
    }

    /**
     * 输出审查结果
     */
    private function outputResults(): void
    {
        if ($this->option('json')) {
            $this->line(json_encode([
                'stats' => $this->stats,
                'findings' => $this->findings,
                'reviewed_at' => now()->toDateTimeString(),
            ], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT));
            return;
        }

        $this->newLine();
        $this->line('──────────────────────────────────────────');
        $this->line('  审查统计');
        $this->line('──────────────────────────────────────────');
        $this->line("  角色总数：{$this->stats['total_roles']}");
        $this->line("  权限总数：{$this->stats['total_permissions']}");
        $this->line("  用户总数：{$this->stats['total_users']}");
        $this->line("  发现问题：{$this->stats['issues']}");
        if ($this->stats['fixed'] > 0) {
            $this->line("  已修复：{$this->stats['fixed']}");
        }
        $this->newLine();

        if (empty($this->findings)) {
            $this->info('  未发现异常权限分配，审查通过。');
            return;
        }

        $this->line('──────────────────────────────────────────');
        $this->line('  审查清单');
        $this->line('──────────────────────────────────────────');
        $this->newLine();

        foreach ($this->findings as $finding) {
            $severityIcon = match ($finding['severity']) {
                'error' => '红色',
                'warning' => '黄色',
                default => '信息',
            };
            $this->line("  [{$severityIcon}] {$finding['message']}");
            $this->line("  建议：{$finding['action']}");
            $this->newLine();
        }
    }

    /**
     * 通知管理员
     */
    private function notifyAdmins(): void
    {
        $this->info('通知管理员...');

        $userModel = config('kylin-rbac.user_model', \App\Models\User::class);
        if (! class_exists($userModel)) {
            return;
        }

        try {
            $admins = $userModel::whereHas('roles', function ($query) {
                $query->whereIn('name', ['super_admin', 'admin']);
            })->get();

            if ($admins->isEmpty()) {
                $this->warn('  未找到管理员用户');
                return;
            }

            if (class_exists(\Kylin\Notification\Models\Notification::class)) {
                foreach ($admins as $admin) {
                    \Kylin\Notification\Models\Notification::create([
                        'user_id' => $admin->id,
                        'type' => 'permission_review',
                        'title' => '权限定期审查报告',
                        'content' => "权限定期审查完成，发现 {$this->stats['issues']} 个问题，请及时处理。",
                        'data' => [
                            'stats' => $this->stats,
                            'findings' => $this->findings,
                        ],
                        'is_read' => false,
                    ]);
                }
            }

            $this->info("  已通知 {$admins->count()} 位管理员");
        } catch (\Throwable $e) {
            $this->warn('  通知发送失败：' . $e->getMessage());
        }
    }
}