from functools import wraps, partial
from typing import Callable, Any
from fastapi import Request, HTTPException, status
from mwsdk import Rightmanage_inner
import logging
import asyncio

# 类型别名
AsyncDependency = Callable[[Request], Any]


class PermissionError(Exception):
    pass


class FastAPIPermission:
    def __init__(self, sysname: str | None = None, version: str = 'v1.0'):
        '''
        :param sysname: 系统名称，如:maxguideweb
        :param version: 权限的版本，通过url取permission
        '''
        super().__init__()
        self.version: str = version
        self.sysname: str | None = sysname

    def _get_system_name(self, request: Request) -> str:
        '''从请求中获取系统名称（支持从 state 或 config 读取）'''
        # 如果初始化时指定了 sysname，直接使用
        if self.sysname:
            return self.sysname
        
        # 否则从应用配置中读取
        if hasattr(request.app.state, 'SYSTEM_NAME'):
            return request.app.state.SYSTEM_NAME
        elif hasattr(request.app, 'config') and hasattr(request.app.config, 'SYSTEM_NAME'):
            return request.app.config.SYSTEM_NAME
        
        # 都没有则返回空字符串
        return ''

    def _is_development(self, request: Request) -> bool:
        '''检查是否为开发环境'''
        if hasattr(request.app.state, 'DEVELOPMENT'):
            return request.app.state.DEVELOPMENT
        elif hasattr(request.app, 'config') and hasattr(request.app.config, 'DEVELOPMENT'):
            return request.app.config.DEVELOPMENT
        return False

    def _get_user_id(self, request: Request) -> int | None:
        '''从请求中获取用户ID'''
        return getattr(request.state, 'user_id', None)

    def _get_user_name(self, request: Request) -> str:
        '''从请求中获取用户名'''
        return getattr(request.state, 'user_name', 'unknown')

    def _should_skip_permission_check(self, request: Request) -> bool:
        '''判断是否应该跳过权限检查（开发模式或非appuser用户）'''
        # 开发环境跳过
        if self._is_development(request):
            return True
        
        # 非appuser类型用户跳过权限检查
        if hasattr(request.state, 'current_user'):
            current_user = request.state.current_user
            if hasattr(current_user, 'type') and current_user.type != 'appuser':
                return True
        
        return False

    async def _get_permission(self, request: Request) -> dict[str, Any]:
        '''获取用户权限（异步包装同步SDK调用）'''
        # 开发模式下返回空权限（跳过检查）
        if self._is_development(request):
            return {}
        
        user_id = self._get_user_id(request)
        if not user_id:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not authenticated")
        
        try:
            # 使用 run_in_executor 在线程池中执行同步SDK调用，避免阻塞事件循环
            loop = asyncio.get_running_loop()
            # 使用 partial 正确传递多个参数
            func = partial(Rightmanage_inner().permissions, self._get_system_name(request), user_id, self.version)
            _, result = await loop.run_in_executor(None, func)
            return result
        except HTTPException:
            raise
        except Exception as e:
            logging.error(f'获取权限服务出错，错误：{str(e)}')
            raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Permission service unavailable")

    def check(
        self, 
        subsystem3s: str | list[str], 
        actions: list[str]
    ) -> AsyncDependency:
        '''
        创建权限检查的依赖项，用于FastAPI路由
        :param subsystem3s: 权限名称，比如："vehicle" or "vehicle,car" or ["vehicle","car"] 等
        :param actions: 是list，内容为 ['insert','edit','delete',...]
        :return: FastAPI依赖函数
        
        使用示例:
            @app.get("/employees")
            async def get_employees(
                _: None = Depends(permission.check('employee', ['view']))
            ):
                pass
        '''
        async def check_permission(request: Request):
            # 开发环境或特殊用户跳过权限检查
            if self._should_skip_permission_check(request):
                return
            
            # 处理subsystem3s参数
            if isinstance(subsystem3s, str):
                subsystem3list = subsystem3s.split(',')
            elif isinstance(subsystem3s, list):
                subsystem3list = subsystem3s
            else:
                raise HTTPException(
                    status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                    detail=f'the subsystem {subsystem3s} is not support, example:"vehicle" or "vehicle,car" or ["vehicle","car"]'
                )
            
            # 获取权限（带重试机制）
            try:
                permissions = await self._get_permission(request)
            except HTTPException as e:
                # 避免第一次访问时出错，再次重试访问权限
                if e.status_code == status.HTTP_503_SERVICE_UNAVAILABLE:
                    logging.error(f'第一次访问权限服务出错，进行重试')
                    try:
                        permissions = await self._get_permission(request)
                    except Exception as retry_error:
                        # 避免出500错误，返回403
                        logging.error(f'第二次访问权限服务出错，错误：{str(retry_error)}')
                        raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail="Permission denied")
                else:
                    raise
            
            # 没有指定 subsystem 和 actions，表示允许访问
            if permissions and not subsystem3list and not actions:
                return
            
            # 检查权限
            acts_sup = set()
            sys_name = self._get_system_name(request)
            for subsystem3 in subsystem3list:
                permission = permissions.get(subsystem3)
                if permission is None:
                    # 支持同时检测多个权限后，有些权限可能不会在当前项目中
                    logging.warning(f'the subsystem({subsystem3}) is not in {sys_name}')
                    continue
                # 获取权限的操作
                ops = permission.get('ops', [])
                acts_sup.update({act for act in actions if act in ops})
            
            # 权限没有操作时，则拒绝
            if not acts_sup:
                raise HTTPException(
                    status_code=status.HTTP_403_FORBIDDEN,
                    detail=f"Permission denied: missing required actions {actions} for {subsystem3s}"
                )
        
        return check_permission

    async def check_permission(self, subsystem3: str, action: str, msg: str = '', request: Request | None = None) -> bool:
        '''
        检查权限的方法，用于在函数内部调用（异步包装同步SDK调用）
        :param subsystem3: 子系统名称
        :param action: 操作类型
        :param msg: 自定义错误消息
        :param request: FastAPI Request对象
        
        使用示例:
            @app.get("/employee/check")
            async def check_employee_auth(request: Request):
                # 检查是否有看到身份证的权限
                await permission.check_permission('employee', 'see_ID', request=request)
        '''
        if not request:
            raise HTTPException(
                status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                detail="Request object is required for permission check"
            )
        
        user_id = self._get_user_id(request)
        if not user_id:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="User not authenticated")
        
        try:
            # 使用 run_in_executor 在线程池中执行同步SDK调用
            loop = asyncio.get_running_loop()
            func = partial(Rightmanage_inner().permissions, self._get_system_name(request), user_id)
            _, permissions_js = await loop.run_in_executor(None, func)
            permission = permissions_js.get(subsystem3)
        except HTTPException:
            raise
        except Exception as e:
            logging.error(f'获取权限服务出错，错误：{str(e)}')
            raise HTTPException(status_code=status.HTTP_503_SERVICE_UNAVAILABLE, detail="Permission service unavailable")
        
        sys_name = self._get_system_name(request)
        if permission is None:
            raise HTTPException(
                status_code=status.HTTP_404_NOT_FOUND,
                detail=f'the subsystem({subsystem3}) is not exist in {sys_name}'
            )
        
        ops = permission.get('ops', [])
        if action not in ops:
            if not msg:
                user_name = self._get_user_name(request)
                msg = f'The user({user_name}) have no this ({action}) right!'
            raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=msg)
        return True

    def create_decorator(self, subsystem3s: str | list[str], actions: list[str]) -> Callable[..., Any]:
        '''
        创建装饰器风格的权限检查（与Flask版本API兼容）
        :param subsystem3s: 权限名称
        :param actions: 操作列表
        
        使用示例:
            @permission.create_decorator('employee', ['view'])
            async def get_employees():
                pass
        '''
        def decorate(func: Callable[..., Any]) -> Callable[..., Any]:
            @wraps(func)
            async def wrapper(*args: Any, **kwargs: Any):
                # 获取request对象（假设在第一个参数或通过其他方式）
                request = None
                if args and isinstance(args[0], Request):
                    request = args[0]
                elif 'request' in kwargs:
                    request = kwargs['request']
                
                if request:
                    dependency = self.check(subsystem3s, actions)
                    await dependency(request)
                else:
                    raise HTTPException(
                        status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
                        detail="Request object not found for permission check"
                    )
                
                # 处理同步和异步函数
                if asyncio.iscoroutinefunction(func):
                    return await func(*args, **kwargs)
                else:
                    return func(*args, **kwargs)
            return wrapper
        return decorate
