<?php

namespace App\Services;

use App\Enums\Status;
use App\Http\Responses\ApiResponse;
use App\Models\AdminMenu;
use App\Models\AdminPermission;
use App\Models\AdminRole;
use App\Models\AdminUser;
use App\Models\AppModel;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\DB;

class RoleService
{
    public function index(array $params, ?AdminUser $user = null)
    {
        $query = AdminRole::query();

        // 非超权账号：只能看到自身角色分支下的角色（自身 + 子孙），看不到父级及其他同级角色
        if ($user && ! $this->isSuperPrivileged($user)) {
            $manageableIds = $this->getManageableRoleIds($user);
            if (empty($manageableIds)) {
                // 无任何角色关联，返回空分页
                return ApiResponse::paginate(AdminRole::whereRaw('1=0')->paginate());
            }
            $query->whereIn('id', $manageableIds);
        }

        if (isset($params['keyword']) && ! empty($params['keyword'])) {
            $query->where(function ($q) use ($params) {
                $q->where('name', 'like', "%{$params['keyword']}%")
                    ->orWhere('code', 'like', "%{$params['keyword']}%");
            });
        }

        if (isset($params['status']) && $params['status'] !== '') {
            $query->where('status', $params['status']);
        }

        $perPage = $params['per_page'] ?? config('cmspro.pagination.per_page', 15);
        $roles = $query->orderBy('sort', 'asc')->orderBy('id', 'desc')->paginate($perPage);

        return ApiResponse::paginate($roles);
    }

    public function tree(?AdminUser $user = null)
    {
        $query = AdminRole::where('status', 1);

        // 非超权账号：只能看到自身角色分支下的角色
        if ($user && ! $this->isSuperPrivileged($user)) {
            $manageableIds = $this->getManageableRoleIds($user);
            if (empty($manageableIds)) {
                return ApiResponse::success([]);
            }
            $query->whereIn('id', $manageableIds);
        }

        $roles = $query->orderBy('sort', 'asc')->get();
        $tree = $this->buildTree($roles->toArray());

        return ApiResponse::success($tree);
    }

    protected function buildTree(array $items, int $parentId = 0): array
    {
        $tree = [];
        foreach ($items as $item) {
            if (($item['parent_id'] ?? 0) == $parentId) {
                $children = $this->buildTree($items, $item['id']);
                if ($children) {
                    $item['children'] = $children;
                }
                $tree[] = $item;
            }
        }

        return $tree;
    }

    // ============ 新增辅助方法 ============

    /**
     * 判断当前用户是否为超权账号（不受任何限制）
     * 双重保护：
     *   1. id=1 的安装账号（防止 super_admin 角色被误操作后还能兜底）
     *   2. 拥有 super_admin 角色的用户（正常业务场景）
     */
    private function isSuperPrivileged(AdminUser $user): bool
    {
        return $user->id === 1 || $user->isSuperAdmin();
    }

    /**
     * 获取指定角色分支下的所有子角色ID（递归）
     */
    private function getSubRoleIds(int $roleId): array
    {
        $ids = [];
        $children = AdminRole::where('parent_id', $roleId)->pluck('id');
        foreach ($children as $childId) {
            $ids[] = $childId;
            $ids = array_merge($ids, $this->getSubRoleIds($childId));
        }
        return $ids;
    }

    /**
     * 获取指定角色的完整分支ID列表（自身 + 所有子孙）
     */
    private function getBranchRoleIds(int $roleId): array
    {
        return array_merge([$roleId], $this->getSubRoleIds($roleId));
    }

    /**
     * 获取指定角色拥有的权限ID集合
     */
    private function getRolePermissionIds(int $roleId): array
    {
        $role = AdminRole::find($roleId);
        if (! $role) {
            return [];
        }
        return $role->permissions()->pluck('admin_permissions.id')->toArray();
    }

    /**
     * 获取指定角色拥有的菜单ID集合
     */
    private function getRoleMenuIds(int $roleId): array
    {
        $role = AdminRole::find($roleId);
        if (! $role) {
            return [];
        }
        return $role->menus()->pluck('admin_menus.id')->toArray();
    }

    /**
     * 获取非超权用户可管辖的角色ID列表（自身角色 + 其所有子孙分支）
     */
    private function getManageableRoleIds(AdminUser $user): array
    {
        $userRoles = $user->roles;
        $manageableIds = [];
        foreach ($userRoles as $role) {
            $manageableIds = array_merge($manageableIds, $this->getBranchRoleIds($role->id));
        }
        return array_unique($manageableIds);
    }

    /**
     * 获取权限分组的显示名称（中文）
     *
     * 系统模块使用固定映射表，应用模块从 apps 表查询应用名称。
     */
    private function getModuleDisplayName(string $module, ?string $appId = null): string
    {
        // 系统模块映射
        $systemModuleMap = [
            'user'          => '用户管理',
            'role'          => '角色管理',
            'menu'          => '菜单管理',
            'permission'    => '权限管理',
            'config'        => '系统配置',
            'setting'       => '系统设置',
            'attachment'    => '附件管理',
            'log'           => '系统日志',
            'operation-log' => '操作日志',
            'database'      => '数据管理',
            'front-user'    => '前台用户',
            'user-group'    => '用户组',
        ];

        if (isset($systemModuleMap[$module])) {
            return $systemModuleMap[$module];
        }

        // 应用模块：从 apps 表查询应用名称
        if ($appId) {
            $appName = AppModel::where('app_id', $appId)->value('name');
            if ($appName) {
                return $appName;
            }
        }

        // 兜底返回 module 值
        return $module;
    }

    /**
     * 为权限集合附加 module_name 显示名
     */
    private function attachModuleName($permissions): void
    {
        foreach ($permissions as $perm) {
            $perm->module_name = $this->getModuleDisplayName($perm->module, $perm->app_id);
        }
    }

    /**
     * 清除指定角色下所有用户的权限缓存
     *
     * 当角色的权限、菜单、状态等发生变更时，需刷新该角色关联用户的权限缓存，
     * 确保 CheckPermission 中间件下次校验时重新加载最新权限。
     */
    private function clearRoleUsersPermissionCache(int $roleId): void
    {
        $userIds = DB::table('admin_user_roles')
            ->where('role_id', $roleId)
            ->pluck('user_id');

        foreach ($userIds as $userId) {
            Cache::forget("user:{$userId}:permissions");
        }
    }

    // ============ 业务方法 ============

    /**
     * 创建角色
     *
     * @param array $data
     * @param AdminUser|null $user 当前登录用户
     */
    public function store(array $data, ?AdminUser $user = null)
    {
        $parentId = (int) ($data['parent_id'] ?? 0);

        // 非超权账号需要校验 parent_id
        if ($user && ! $this->isSuperPrivileged($user)) {
            $manageableIds = $this->getManageableRoleIds($user);
            if (empty($manageableIds)) {
                return ApiResponse::error(40303, '您没有可管辖的角色，无法创建');
            }

            // 只有超权可以创建顶级角色
            if ($parentId === 0) {
                return ApiResponse::error(40303, '无权创建顶级角色');
            }

            // parent_id 必须在可管辖范围内
            if (! in_array($parentId, $manageableIds)) {
                return ApiResponse::error(40303, '无权为该角色创建子角色');
            }
        }

        $role = AdminRole::create([
            'name' => $data['name'],
            'code' => $data['code'],
            'description' => $data['description'] ?? '',
            'parent_id' => $parentId,
            'sort' => $data['sort'] ?? 0,
            'status' => $data['status'] ?? 1,
        ]);

        if (isset($data['permission_ids']) && ! empty($data['permission_ids'])) {
            $role->permissions()->sync($data['permission_ids']);
        }

        if (isset($data['menu_ids']) && ! empty($data['menu_ids'])) {
            $role->menus()->sync($data['menu_ids']);
        }

        $this->clearRoleUsersPermissionCache($role->id);

        return ApiResponse::success($role, '创建成功');
    }

    public function show(int $id)
    {
        $role = AdminRole::with(['permissions', 'menus'])->find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        return ApiResponse::success($role);
    }

    public function update(int $id, array $data)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        if ($role->code === 'super_admin' && isset($data['code']) && $data['code'] !== 'super_admin') {
            return ApiResponse::error(40303, '超级管理员角色代码不可修改');
        }

        $role->update([
            'name' => $data['name'],
            'code' => $data['code'] ?? $role->code,
            'description' => $data['description'] ?? '',
            'parent_id' => $data['parent_id'] ?? 0,
            'sort' => $data['sort'] ?? 0,
            'status' => $data['status'] ?? 1,
        ]);

        if (isset($data['permission_ids'])) {
            $role->permissions()->sync($data['permission_ids']);
        }

        if (isset($data['menu_ids'])) {
            $role->menus()->sync($data['menu_ids']);
        }

        $this->clearRoleUsersPermissionCache($id);

        return ApiResponse::success($role, '更新成功');
    }

    public function destroy(int $id)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        if ($role->code === 'super_admin') {
            return ApiResponse::error(40303, '超级管理员角色不可删除');
        }

        if ($role->users()->exists()) {
            return ApiResponse::error(40303, '该角色下有用户，不可删除');
        }

        if (AdminRole::where('parent_id', $id)->exists()) {
            return ApiResponse::error(40303, '该角色下有子角色，不可删除');
        }

        DB::transaction(function () use ($role) {
            $role->permissions()->detach();
            $role->menus()->detach();
            $role->delete();
        });

        $this->clearRoleUsersPermissionCache($id);

        return ApiResponse::success(null, '删除成功');
    }

    /**
     * 获取角色的权限列表（含可分配候选权限）
     *
     * @param int $id
     * @param AdminUser|null $user 当前登录用户
     */
    public function getPermissions(int $id, ?AdminUser $user = null)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        $selectedIds = $role->permissions()->pluck('admin_permissions.id')->toArray();

        // 判断是否为编辑自身角色（非超权时禁止编辑自己所属角色的权限）
        $isOwnRole = $user && ! $this->isSuperPrivileged($user) && $user->roles->contains('id', $role->id);

        // 如果是自身角色，无论 parent_id 是多少，都返回数据 + is_own_role=true
        // 前端会显示遮罩提示，而不是直接报错
        if ($isOwnRole) {
            $allPermissions = AdminPermission::where('status', Status::ENABLED)
                ->orderBy('module')
                ->orderBy('id')
                ->get();

            $this->attachModuleName($allPermissions);

            return ApiResponse::success([
                'all_permissions' => $allPermissions,
                'selected_ids' => $selectedIds,
                'is_own_role' => true,
            ]);
        }

        // 超权账号：返回全部启用的权限
        if ($user && $this->isSuperPrivileged($user)) {
            $allPermissions = AdminPermission::where('status', Status::ENABLED)
                ->orderBy('module')
                ->orderBy('id')
                ->get();

            $this->attachModuleName($allPermissions);

            return ApiResponse::success([
                'all_permissions' => $allPermissions,
                'selected_ids' => $selectedIds,
                'is_own_role' => false,
            ]);
        }

        // 非超权：校验操作资格
        $manageableIds = $this->getManageableRoleIds($user);
        if (! in_array($role->id, $manageableIds)) {
            return ApiResponse::error(40303, '无权查看该角色的权限');
        }

        // 顶级角色只有超权可以操作
        if ($role->parent_id === 0) {
            return ApiResponse::error(40303, '无权查看顶级角色的权限');
        }

        // 只返回父角色拥有的权限作为可勾选列表
        $parentPermissionIds = $this->getRolePermissionIds($role->parent_id);
        $allPermissions = AdminPermission::whereIn('id', $parentPermissionIds)
            ->where('status', Status::ENABLED)
            ->orderBy('module')
            ->orderBy('id')
            ->get();

        $this->attachModuleName($allPermissions);

        return ApiResponse::success([
            'all_permissions' => $allPermissions,
            'selected_ids' => $selectedIds,
            'is_own_role' => $isOwnRole,
        ]);
    }

    /**
     * 更新角色的权限
     *
     * @param int $id
     * @param array $permissionIds
     * @param AdminUser|null $user 当前登录用户
     */
    public function updatePermissions(int $id, array $permissionIds, ?AdminUser $user = null)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        // 非超权：走完整校验
        if ($user && ! $this->isSuperPrivileged($user)) {
            // ① 禁止编辑自身角色的权限
            if ($user->roles->contains('id', $role->id)) {
                return ApiResponse::error(40303, '不允许编辑自己所在角色的权限');
            }

            // ② 校验操作资格（被修改角色必须在自己的分支内）
            $manageableIds = $this->getManageableRoleIds($user);
            if (! in_array($role->id, $manageableIds)) {
                return ApiResponse::error(40303, '无权修改该角色的权限');
            }

            // ③ 顶级角色只有超权可以修改
            if ($role->parent_id === 0) {
                return ApiResponse::error(40303, '无权修改顶级角色的权限');
            }

            // ④ 分配的权限不能超过父角色拥有的
            $parentPermissionIds = $this->getRolePermissionIds($role->parent_id);
            $exceeded = array_diff($permissionIds, $parentPermissionIds);
            if (! empty($exceeded)) {
                return ApiResponse::error(40303, '分配的权限超出了父角色的权限范围');
            }

            // ⑤ 操作者自身也必须拥有这些权限
            $operatorPermIds = [];
            foreach ($user->roles as $ur) {
                $operatorPermIds = array_merge($operatorPermIds, $this->getRolePermissionIds($ur->id));
            }
            $operatorPermIds = array_unique($operatorPermIds);
            $exceededOp = array_diff($permissionIds, $operatorPermIds);
            if (! empty($exceededOp)) {
                return ApiResponse::error(40303, '您自身没有这些权限，无法分配');
            }
        }

        $role->permissions()->sync($permissionIds);

        $this->clearRoleUsersPermissionCache($id);

        return ApiResponse::success(null, '权限分配成功');
    }

    /**
     * 获取角色的菜单列表（含可分配候选菜单）
     *
     * @param int $id
     * @param AdminUser|null $user 当前登录用户
     */
    public function getMenus(int $id, ?AdminUser $user = null)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        $menuIds = $role->menus()->pluck('admin_menus.id')->toArray();

        // 判断是否为编辑自身角色（非超权时禁止编辑自己所属角色的菜单）
        $isOwnRole = $user && ! $this->isSuperPrivileged($user) && $user->roles->contains('id', $role->id);

        // 如果是自身角色，无论 parent_id 是多少，都返回数据 + is_own_role=true
        // 前端会显示遮罩提示，而不是直接报错
        if ($isOwnRole) {
            $allMenus = AdminMenu::orderBy('sort', 'asc')->get();

            return ApiResponse::success([
                'all_menus' => $allMenus,
                'selected_ids' => $menuIds,
                'is_own_role' => true,
            ]);
        }

        // 超权账号：返回全部菜单
        if ($user && $this->isSuperPrivileged($user)) {
            $allMenus = AdminMenu::orderBy('sort', 'asc')->get();

            return ApiResponse::success([
                'all_menus' => $allMenus,
                'selected_ids' => $menuIds,
                'is_own_role' => false,
            ]);
        }

        // 非超权：校验操作资格
        $manageableIds = $this->getManageableRoleIds($user);
        if (! in_array($role->id, $manageableIds)) {
            return ApiResponse::error(40303, '无权查看该角色的菜单');
        }

        // 顶级角色只有超权可以操作
        if ($role->parent_id === 0) {
            return ApiResponse::error(40303, '无权查看顶级角色的菜单');
        }

        // 只返回父角色拥有的菜单作为可勾选列表
        $parentMenuIds = $this->getRoleMenuIds($role->parent_id);
        $allMenus = AdminMenu::whereIn('id', $parentMenuIds)
            ->orderBy('sort', 'asc')
            ->get();

        return ApiResponse::success([
            'all_menus' => $allMenus,
            'selected_ids' => $menuIds,
            'is_own_role' => $isOwnRole,
        ]);
    }

    /**
     * 更新角色的菜单
     *
     * @param int $id
     * @param array $menuIds
     * @param AdminUser|null $user 当前登录用户
     */
    public function updateMenus(int $id, array $menuIds, ?AdminUser $user = null)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        // 非超权：走完整校验
        if ($user && ! $this->isSuperPrivileged($user)) {
            // ① 禁止编辑自身角色的菜单
            if ($user->roles->contains('id', $role->id)) {
                return ApiResponse::error(40303, '不允许编辑自己所在角色的菜单');
            }

            // ② 校验操作资格（被修改角色必须在自己的分支内）
            $manageableIds = $this->getManageableRoleIds($user);
            if (! in_array($role->id, $manageableIds)) {
                return ApiResponse::error(40303, '无权修改该角色的菜单');
            }

            // ③ 顶级角色只有超权可以修改
            if ($role->parent_id === 0) {
                return ApiResponse::error(40303, '无权修改顶级角色的菜单');
            }

            // ④ 分配的菜单不能超过父角色拥有的
            $parentMenuIds = $this->getRoleMenuIds($role->parent_id);
            $exceeded = array_diff($menuIds, $parentMenuIds);
            if (! empty($exceeded)) {
                return ApiResponse::error(40303, '分配的菜单超出了父角色的菜单范围');
            }

            // ⑤ 操作者自身也必须拥有这些菜单
            $operatorMenuIds = [];
            foreach ($user->roles as $ur) {
                $operatorMenuIds = array_merge($operatorMenuIds, $this->getRoleMenuIds($ur->id));
            }
            $operatorMenuIds = array_unique($operatorMenuIds);
            $exceededOp = array_diff($menuIds, $operatorMenuIds);
            if (! empty($exceededOp)) {
                return ApiResponse::error(40303, '您自身没有这些菜单，无法分配');
            }
        }

        $role->menus()->sync($menuIds);

        $this->clearRoleUsersPermissionCache($id);

        return ApiResponse::success(null, '菜单分配成功');
    }

    /**
     * 获取所有启用角色（父角色候选列表）
     *
     * 超权账号：返回全部
     * 普通角色：只返回自己分支下的角色（自身角色 + 所有子孙）
     *
     * 返回结构附带 is_super_privileged 字段，供前端决定是否显示"顶级角色"选项
     * （非超权账号禁止创建顶级角色，见 store() 校验）
     *
     * @param AdminUser|null $user 当前登录用户
     */
    public function all(?AdminUser $user = null)
    {
        $isSuperPrivileged = $user && $this->isSuperPrivileged($user);

        $query = AdminRole::where('status', 1);

        // 非超权账号：只返回自己分支下的角色
        if ($user && ! $isSuperPrivileged) {
            $manageableIds = $this->getManageableRoleIds($user);
            if (empty($manageableIds)) {
                return ApiResponse::success([
                    'items' => [],
                    'is_super_privileged' => false,
                ]);
            }
            $query->whereIn('id', $manageableIds);
        }

        $roles = $query->orderBy('sort', 'asc')
            ->get(['id', 'name', 'code', 'parent_id']);

        return ApiResponse::success([
            'items' => $roles,
            'is_super_privileged' => $isSuperPrivileged,
        ]);
    }

    public function updateStatus(int $id, int $status)
    {
        $role = AdminRole::find($id);

        if (! $role) {
            return ApiResponse::error(40302, '角色不存在');
        }

        if ($role->code === 'super_admin' && $status === 0) {
            return ApiResponse::error(40303, '不能禁用超级管理员角色');
        }

        $role->update(['status' => $status]);

        $this->clearRoleUsersPermissionCache($id);

        return ApiResponse::success($role, $status === 1 ? '启用成功' : '禁用成功');
    }
}
