<?php
declare(strict_types=1);

/**
 * 计算某个系统用户当前生效的权限集合，登录时算一次缓存进 SESSION，不追求实时生效
 *
 * 有效角色 = 用户自己绑定的角色 UNION 所在所有部门绑定的角色
 * 多角色对同一权限码的合并规则：
 *   - types：并集
 *   - data_scope：取数值更大的（1=仅自己 < 2=本部门查看/自己编辑 < 3=本部门查看/本部门编辑，数值越大越宽松）
 *   - extra_values：同名字段，数值取较大值，布尔取真，其它取第一个非空值
 * "本部门" 在多部门用户身上按所属所有部门的并集计算（不是只认一个主部门）
 */
final class PermissionResolver
{
    /**
     * @return array{permissions: array<string, array{types: array<string>, dataScope: int, extra: array}>, depaIds: array<int>}
     */
    public static function resolveForAdmin(int $adminId): array
    {
        $depaIds = (new Users())->depaIdsOf($adminId);

        $roleModel = new Role();
        $roleIds = $roleModel->roleIdsOfAdmin($adminId);
        foreach ($depaIds as $depaId) {
            $roleIds = array_merge($roleIds, $roleModel->roleIdsOfDepa($depaId));
        }
        $roleIds = array_values(array_unique($roleIds));

        $permissions = [];
        foreach ($roleIds as $roleId) {
            foreach ($roleModel->permissionsOf($roleId) as $bind) {
                self::mergeInto($permissions, $bind);
            }
        }

        return ['permissions' => $permissions, 'depaIds' => $depaIds];
    }

    private static function mergeInto(array &$permissions, array $bind): void
    {
        $code = $bind['sys_role_permission_lnk_permission_code'];
        $types = json_decode($bind['sys_role_permission_types'], true) ?: [];
        $dataScope = (int) $bind['sys_role_permission_data_scope'];
        $extra = $bind['sys_role_permission_extra_values'] ? (json_decode($bind['sys_role_permission_extra_values'], true) ?: []) : [];

        if (!isset($permissions[$code])) {
            $permissions[$code] = ['types' => [], 'dataScope' => 0, 'extra' => []];
        }

        $permissions[$code]['types'] = array_values(array_unique(array_merge($permissions[$code]['types'], $types)));
        $permissions[$code]['dataScope'] = max($permissions[$code]['dataScope'], $dataScope);
        $permissions[$code]['extra'] = self::mergeExtra($permissions[$code]['extra'], $extra);
    }

    private static function mergeExtra(array $a, array $b): array
    {
        foreach ($b as $field => $value) {
            if (!array_key_exists($field, $a)) {
                $a[$field] = $value;
                continue;
            }
            $existing = $a[$field];
            if (is_numeric($existing) && is_numeric($value)) {
                $a[$field] = max((float) $existing, (float) $value);
            } elseif (is_bool($existing) || is_bool($value)) {
                $a[$field] = $existing || $value;
            }
            // 其它类型（字符串等）保留已有值，不覆盖
        }

        return $a;
    }
}
