<?php

namespace Kylin\Rbac\Services;

use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Log;
use Kylin\Rbac\Models\Permission;
use Kylin\Rbac\Models\Role;

class RbacService
{
    public function createRole(string $name, ?string $displayName = null, ?string $guardName = null, ?string $description = null): Role
    {
        $role = Role::create([
            'name' => $name,
            'display_name' => $displayName ?? $name,
            'guard_name' => $guardName ?? config('kylin-rbac.guard', 'web'),
            'description' => $description,
        ]);

        $this->audit('role_created', $role->id, [
            'name' => $name,
            'guard_name' => $role->guard_name,
        ]);

        return $role;
    }

    public function createPermission(string $name, ?string $displayName = null, ?string $module = null, ?string $guardName = null, ?string $description = null): Permission
    {
        $permission = Permission::create([
            'name' => $name,
            'display_name' => $displayName ?? $name,
            'module' => $module,
            'guard_name' => $guardName ?? config('kylin-rbac.guard', 'web'),
            'description' => $description,
        ]);

        $this->audit('permission_created', $permission->id, [
            'name' => $name,
            'module' => $module,
            'guard_name' => $permission->guard_name,
        ]);

        return $permission;
    }

    public function syncRolePermissions(Role $role, array $permissionNames): void
    {
        $permissions = Permission::whereIn('name', $permissionNames)
            ->where('guard_name', $role->guard_name)
            ->get();
        $role->permissions()->sync($permissions->pluck('id'));

        $this->audit('role_permissions_synced', $role->id, [
            'role_name' => $role->name,
            'permission_count' => $permissions->count(),
            'permission_names' => $permissionNames,
        ]);
    }

    public function getRolePermissions(string $roleName, ?string $guardName = null): array
    {
        $guardName = $guardName ?? config('kylin-rbac.guard', 'web');
        $cacheKey = config('kylin-rbac.cache.key', 'kylin.rbac') . ".role_perms.{$guardName}.{$roleName}";
        $ttl = config('kylin-rbac.cache.ttl', 86400);

        if (! config('kylin-rbac.cache.enabled', true)) {
            return $this->resolveRolePermissions($roleName, $guardName);
        }

        return Cache::remember($cacheKey, $ttl, fn () => $this->resolveRolePermissions($roleName, $guardName));
    }

    private function resolveRolePermissions(string $roleName, string $guardName): array
    {
        $role = Role::findByName($roleName, $guardName);
        if ($role === null) {
            return [];
        }
        return $role->permissions->pluck('name')->toArray();
    }

    /**
     * 记录 RBAC 审计日志
     *
     * 依据《网络安全法》和等保 2.0 (GB/T 22239) 8.1.4.4，
     * 角色和权限的创建、变更需记录审计日志，确保权限变更可追溯。
     */
    private function audit(string $event, int $modelId, array $extra = []): void
    {
        if (! class_exists(\Kylin\AuditLog\Models\AuditLog::class)) {
            return;
        }

        try {
            \Kylin\AuditLog\Models\AuditLog::create([
                'event' => $event,
                'model_type' => 'rbac',
                'model_id' => $modelId,
                'new_values' => $extra,
                'old_values' => null,
                'user_id' => auth()->id(),
                'ip' => request()?->ip(),
                'url' => request()?->fullUrl(),
                'tag' => 'rbac',
            ]);
        } catch (\Throwable $e) {
            Log::warning('RBAC 审计日志写入失败', [
                'error' => $e->getMessage(),
                'event' => $event,
                'model_id' => $modelId,
            ]);
        }
    }
}
