<?php

declare(strict_types=1);

namespace Kylin\Rbac\Services;

use Kylin\Rbac\Models\Role;

/**
 * 权限变更通知服务
 *
 * 依据等保 2.0 (GB/T 22239) 8.1.4.1：
 * 敏感权限变更后需通知相关人员。
 *
 * 通知场景：
 *  - 角色分配/移除
 *  - 权限授予/撤销
 *  - 角色创建/删除
 *  - 权限创建/删除
 *
 * 使用方式：
 *   PermissionChangeNotifier::notifyRoleChange('assign', $user, $role);
 *   PermissionChangeNotifier::notifyPermissionChange('grant', $user, $permissionName);
 */
final class PermissionChangeNotifier
{
    /**
     * 敏感权限列表（变更时必须通知）
     */
    private const SENSITIVE_PERMISSIONS = [
        'admin.access',
        'user.delete',
        'rbac.manage',
        'settings.manage',
        'audit-log.view',
        'system.config',
    ];

    /**
     * 敏感角色列表（变更时必须通知）
     */
    private const SENSITIVE_ROLES = [
        'super_admin',
        'admin',
        'auditor',
    ];

    /**
     * 通知角色变更
     *
     * @param string $action 操作类型：assign/remove
     * @param object $user 被操作的用户
     * @param Role $role 角色
     */
    public static function notifyRoleChange(string $action, object $user, Role $role): void
    {
        if (! self::isSensitiveRole($role->name)) {
            return;
        }

        $actionLabel = $action === 'assign' ? '分配' : '移除';
        $message = sprintf(
            '【权限变更通知】管理员 %s 于 %s 为用户 %s (ID: %s) %s了敏感角色 "%s"',
            auth()->user()?->name ?? '系统',
            now()->toDateTimeString(),
            $user->name ?? '未知',
            $user->id ?? 0,
            $actionLabel,
            $role->name,
        );

        self::sendNotification($message, [
            'event' => "role_{$action}",
            'user_id' => $user->id ?? 0,
            'user_name' => $user->name ?? '未知',
            'role_name' => $role->name,
            'operator' => auth()->user()?->name ?? '系统',
            'operator_id' => auth()->id() ?? 0,
            'timestamp' => now()->toDateTimeString(),
        ]);
    }

    /**
     * 通知权限变更
     *
     * @param string $action 操作类型：grant/revoke
     * @param object $user 被操作的用户
     * @param string $permissionName 权限名称
     */
    public static function notifyPermissionChange(string $action, object $user, string $permissionName): void
    {
        if (! self::isSensitivePermission($permissionName)) {
            return;
        }

        $actionLabel = $action === 'grant' ? '授予' : '撤销';
        $message = sprintf(
            '【权限变更通知】管理员 %s 于 %s 为用户 %s (ID: %s) %s了敏感权限 "%s"',
            auth()->user()?->name ?? '系统',
            now()->toDateTimeString(),
            $user->name ?? '未知',
            $user->id ?? 0,
            $actionLabel,
            $permissionName,
        );

        self::sendNotification($message, [
            'event' => "permission_{$action}",
            'user_id' => $user->id ?? 0,
            'user_name' => $user->name ?? '未知',
            'permission' => $permissionName,
            'operator' => auth()->user()?->name ?? '系统',
            'operator_id' => auth()->id() ?? 0,
            'timestamp' => now()->toDateTimeString(),
        ]);
    }

    /**
     * 判断是否为敏感角色
     */
    private static function isSensitiveRole(string $roleName): bool
    {
        return in_array($roleName, self::SENSITIVE_ROLES, true);
    }

    /**
     * 判断是否为敏感权限
     */
    private static function isSensitivePermission(string $permissionName): bool
    {
        foreach (self::SENSITIVE_PERMISSIONS as $sensitive) {
            if (str_starts_with($permissionName, $sensitive)) {
                return true;
            }
        }
        return false;
    }

    /**
     * 发送通知
     *
     * 通知渠道：日志 + 数据库通知（如已集成通知系统）
     */
    private static function sendNotification(string $message, array $context): void
    {
        // 1. 系统日志记录
        logger()->warning($message, $context);

        // 2. 数据库通知（如已集成 kylin/notification）
        if (class_exists(\Kylin\Notification\Models\Notification::class)) {
            try {
                $admins = self::getAdminUsers();
                foreach ($admins as $admin) {
                    \Kylin\Notification\Models\Notification::create([
                        'user_id' => $admin->id,
                        'type' => 'permission_change',
                        'title' => '权限变更通知',
                        'content' => $message,
                        'data' => $context,
                        'is_read' => false,
                    ]);
                }
            } catch (\Throwable $e) {
                logger()->warning('权限变更通知发送失败', [
                    'error' => $e->getMessage(),
                ]);
            }
        }
    }

    /**
     * 获取管理员用户列表
     */
    private static function getAdminUsers(): array
    {
        $userModel = config('kylin-rbac.user_model', \App\Models\User::class);

        if (! class_exists($userModel)) {
            return [];
        }

        try {
            return $userModel::whereHas('roles', function ($query) {
                $query->whereIn('name', self::SENSITIVE_ROLES);
            })->get()->all();
        } catch (\Throwable) {
            return [];
        }
    }
}