<?php

namespace Kylin\Rbac;

use Illuminate\Support\Facades\Gate;
use Illuminate\Support\ServiceProvider;
use Kylin\Rbac\Commands\InstallCommand;
use Kylin\Rbac\Commands\PermissionReviewCommand;
use Kylin\Rbac\Models\Permission;
use Kylin\Rbac\Services\RbacService;

/**
 * RBAC 权限管理服务提供者
 *
 * 注册角色-权限管理服务，包括：
 *  - 服务单例：RbacService（角色/权限 CRUD、缓存、同步）
 *  - Gate 自动注册：为所有权限自动注册 Gate 能力
 *  - 中间件：VerifyRole（角色验证）、VerifyPermission（权限验证）
 *  - 数据库迁移：角色表、权限表、关联表
 *  - 控制台命令：InstallCommand（安装初始化）
 *
 * 依据等保 2.0 (GB/T 22239) 8.1.4.3：基于角色的访问控制
 */
class RbacServiceProvider extends ServiceProvider
{
    /**
     * 注册服务
     */
    public function register(): void
    {
        $this->mergeConfigFrom(
            __DIR__ . '/../config/kylin-rbac.php', 'kylin-rbac'
        );

        $this->app->singleton(RbacService::class);
        $this->app->singleton('kylin-rbac', RbacService::class);
    }

    /**
     * 启动服务
     */
    public function boot(): void
    {
        $this->publishes([
            __DIR__ . '/../config/kylin-rbac.php' => config_path('kylin-rbac.php'),
        ], 'kylin-rbac-config');

        $this->publishes([
            __DIR__ . '/../database/migrations/' => database_path('migrations'),
        ], 'kylin-rbac-migrations');

        $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');

        // 注册 RBAC Gate：为所有权限自动注册 Gate 能力
        // 业务层可通过 Gate::allows('permission.name') 或 $this->authorize() 进行权限校验
        $this->registerRbacGates();

        // 角色验证中间件：检查用户是否拥有指定角色
        $this->app['router']->aliasMiddleware(
            'kylin.role', \Kylin\Rbac\Http\Middleware\VerifyRole::class,
        );
        // 权限验证中间件：检查用户是否拥有指定权限
        $this->app['router']->aliasMiddleware(
            'kylin.permission', \Kylin\Rbac\Http\Middleware\VerifyPermission::class,
        );

        if ($this->app->runningInConsole()) {
            $this->commands([InstallCommand::class, PermissionReviewCommand::class]);
        }
    }

    /**
     * 注册 RBAC Gate 能力
     *
     * 为数据库中所有权限自动注册 Gate，支持：
     *  - Gate::allows('permission.name') 直接权限校验
     *  - 控制器中 $this->authorize('permission.name') 授权校验
     *  - Policy 中通过 checkPermission() 复用
     *
     * 权限表为空时跳过（通常发生在首次安装迁移时）。
     */
    protected function registerRbacGates(): void
    {
        try {
            $permissions = Permission::all();
        } catch (\Throwable) {
            // 数据库表不存在时跳过（首次安装）
            return;
        }

        foreach ($permissions as $permission) {
            Gate::define($permission->name, function ($user) use ($permission) {
                if (! method_exists($user, 'hasPermissionTo')) {
                    return false;
                }
                return $user->hasPermissionTo($permission->name);
            });
        }
    }
}
