"""
Roneia Permission Manager - 权限管理

负责:
- 解析 Skill Manifest 中的权限声明
- 向用户请求授权
- 存储和验证权限状态
- 权限撤销
"""

import asyncio
import json
import logging
from pathlib import Path
from typing import Dict, List, Optional, Any, Callable, Awaitable
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum

logger = logging.getLogger(__name__)


class AuthorizationStatus(Enum):
    """授权状态"""
    PENDING = "pending"      # 等待授权
    GRANTED = "granted"      # 已授权
    DENIED = "denied"        # 已拒绝
    REVOKED = "revoked"      # 已撤销
    EXPIRED = "expired"      # 已过期


@dataclass
class PermissionRequest:
    """权限请求"""
    skill_name: str
    """Skill 名称"""

    permission_type: str
    """权限类型（如 filesystem, network）"""

    scope: str
    """权限范围（如 read:~/Documents）"""

    reason: str
    """请求原因"""

    required: bool
    """是否必需"""

    purpose: str = ""
    """操作目的（如：列出 ~/Documents 目录的内容）"""

    def to_permission_string(self) -> str:
        """转换为权限字符串"""
        if self.scope:
            return f"{self.permission_type}:{self.scope}"
        return self.permission_type

    def to_dict(self) -> Dict[str, Any]:
        return {
            "skill_name": self.skill_name,
            "permission_type": self.permission_type,
            "scope": self.scope,
            "reason": self.reason,
            "required": self.required,
            "purpose": self.purpose,
        }


@dataclass
class PermissionGrant:
    """权限授予记录"""
    permission: str
    """权限字符串"""

    status: AuthorizationStatus
    """授权状态"""

    granted_at: Optional[datetime] = None
    """授权时间"""

    expires_at: Optional[datetime] = None
    """过期时间（可选）"""

    metadata: Dict[str, Any] = field(default_factory=dict)
    """额外元数据"""


# 授权回调类型
AuthorizationCallback = Callable[[List[PermissionRequest]], Awaitable[List[str]]]


class PermissionManager:
    """
    权限管理器

    管理 Skill 的权限授权状态，支持:
    - 解析 Manifest 中的权限声明
    - 交互式用户授权
    - 权限状态持久化
    - 权限撤销和过期
    """

    def __init__(
        self,
        authorization_callback: Optional[AuthorizationCallback] = None,
        storage_path: Optional[Path] = None,
    ):
        """
        初始化权限管理器

        Args:
            authorization_callback: 用户授权回调函数
                接收权限请求列表，返回用户同意的权限字符串列表
            storage_path: 权限状态存储路径（用于持久化）
        """
        self._auth_callback = authorization_callback
        self._storage_path = storage_path

        # skill_name -> permission -> PermissionGrant
        self._grants: Dict[str, Dict[str, PermissionGrant]] = {}

        # 加载持久化的权限状态
        if self._storage_path and self._storage_path.exists():
            self._load_grants()

    # ============================================================================
    # 权限请求与授权
    # ============================================================================

    async def request_permission_on_demand(
        self,
        skill_name: str,
        permission: str,
        reason: str = "",
        purpose: str = "",
    ) -> bool:
        """
        按需请求单个权限（不缓存，每次都询问）

        用于工具执行时动态请求权限，每次调用都会向用户询问。

        Args:
            skill_name: Skill 名称
            permission: 权限字符串，如 "filesystem:read"
            reason: 请求原因（简短说明为什么需要权限）
            purpose: 操作目的（详细说明要执行什么操作，如"列出 ~/Documents 目录的内容"）

        Returns:
            是否授权成功
        """
        if self._auth_callback is None:
            logger.warning(f"No authorization callback configured for {skill_name}")
            return False

        # 解析权限字符串
        parts = permission.split(":", 1)
        perm_type = parts[0]
        scope = parts[1] if len(parts) > 1 else ""

        # 构造权限请求
        request = PermissionRequest(
            skill_name=skill_name,
            permission_type=perm_type,
            scope=scope,
            reason=reason,
            required=True,  # 当前操作必需此权限
            purpose=purpose,
        )

        # 调用授权回调，每次都询问用户
        try:
            granted = await self._auth_callback([request])
        except TaskCancelledException:
            # 任务取消异常需要传播出去
            raise
        except Exception as e:
            logger.error(f"Authorization callback failed: {e}")
            return False

        # 返回是否授权
        return permission in granted

    async def request_permissions(
        self,
        skill_name: str,
        manifest: Dict[str, Any],
    ) -> List[str]:
        """
        请求 Skill 所需权限

        解析 Manifest 中的权限声明，并通过回调函数向用户请求授权。

        Args:
            skill_name: Skill 名称
            manifest: Skill 清单（完整 JSON）

        Returns:
            用户授予的权限字符串列表

        Raises:
            PermissionError: 必需权限被拒绝时抛出
        """
        # 解析权限请求
        requests = self._parse_permission_requests(skill_name, manifest)

        if not requests:
            return []

        # 检查已有授权
        already_granted = []
        needs_authorization = []

        for req in requests:
            perm_str = req.to_permission_string()
            if self.check_permission(skill_name, perm_str):
                already_granted.append(perm_str)
            else:
                needs_authorization.append(req)

        # 如果没有需要新授权的，直接返回
        if not needs_authorization:
            return already_granted

        # 调用授权回调
        if self._auth_callback is None:
            # 没有回调，自动拒绝所有
            raise PermissionError(
                f"No authorization callback configured, cannot grant permissions for {skill_name}"
            )

        granted_perms = await self._auth_callback(needs_authorization)

        # 验证必需权限
        required_perms = [
            req.to_permission_string()
            for req in needs_authorization
            if req.required
        ]

        missing = [p for p in required_perms if p not in granted_perms]
        if missing:
            raise PermissionError(f"Required permissions denied: {missing}")

        # 记录授权状态
        for req in needs_authorization:
            perm_str = req.to_permission_string()
            status = AuthorizationStatus.GRANTED if perm_str in granted_perms else AuthorizationStatus.DENIED

            self._set_grant(skill_name, perm_str, PermissionGrant(
                permission=perm_str,
                status=status,
                granted_at=datetime.utcnow() if status == AuthorizationStatus.GRANTED else None,
            ))

        # 保存状态
        self._save_grants()

        return already_granted + granted_perms

    def _parse_permission_requests(
        self,
        skill_name: str,
        manifest: Dict[str, Any],
    ) -> List[PermissionRequest]:
        """
        从 Manifest 解析权限请求

        Args:
            skill_name: Skill 名称
            manifest: Skill 清单

        Returns:
            权限请求列表
        """
        requests = []
        skill_data = manifest.get("skill", manifest)
        perm_info = skill_data.get("permissions", {})

        # 必需权限
        for perm in perm_info.get("required", []):
            perm_type = perm.get("type", "")
            reason = perm.get("reason", "")

            for scope in perm.get("scope", []):
                requests.append(PermissionRequest(
                    skill_name=skill_name,
                    permission_type=perm_type,
                    scope=scope,
                    reason=reason,
                    required=True,
                ))

        # 可选权限
        for perm in perm_info.get("optional", []):
            perm_type = perm.get("type", "")
            reason = perm.get("reason", "")

            for scope in perm.get("scope", []):
                requests.append(PermissionRequest(
                    skill_name=skill_name,
                    permission_type=perm_type,
                    scope=scope,
                    reason=reason,
                    required=False,
                ))

        return requests

    # ============================================================================
    # 权限检查
    # ============================================================================

    def check_permission(self, skill_name: str, permission: str) -> bool:
        """
        检查权限是否已授予

        Args:
            skill_name: Skill 名称
            permission: 权限字符串

        Returns:
            是否有权限
        """
        skill_grants = self._grants.get(skill_name, {})
        grant = skill_grants.get(permission)

        if grant is None:
            return False

        # 检查状态
        if grant.status != AuthorizationStatus.GRANTED:
            return False

        # 检查过期
        if grant.expires_at and datetime.utcnow() > grant.expires_at:
            grant.status = AuthorizationStatus.EXPIRED
            self._save_grants()
            return False

        return True

    def check_permissions(self, skill_name: str, permissions: List[str]) -> Dict[str, bool]:
        """
        批量检查权限

        Args:
            skill_name: Skill 名称
            permissions: 权限字符串列表

        Returns:
            权限检查结果字典
        """
        return {
            perm: self.check_permission(skill_name, perm)
            for perm in permissions
        }

    def get_granted_permissions(self, skill_name: str) -> List[str]:
        """
        获取已授予的权限列表

        Args:
            skill_name: Skill 名称

        Returns:
            权限字符串列表
        """
        skill_grants = self._grants.get(skill_name, {})
        return [
            perm
            for perm, grant in skill_grants.items()
            if grant.status == AuthorizationStatus.GRANTED
        ]

    # ============================================================================
    # 权限撤销
    # ============================================================================

    async def revoke_permission(self, skill_name: str, permission: str) -> bool:
        """
        撤销权限

        Args:
            skill_name: Skill 名称
            permission: 权限字符串

        Returns:
            是否成功撤销
        """
        if skill_name not in self._grants:
            return False

        if permission not in self._grants[skill_name]:
            return False

        self._grants[skill_name][permission].status = AuthorizationStatus.REVOKED
        self._save_grants()

        logger.info(f"Permission revoked: {skill_name} -> {permission}")
        return True

    async def revoke_all_permissions(self, skill_name: str) -> int:
        """
        撤销 Skill 的所有权限

        Args:
            skill_name: Skill 名称

        Returns:
            撤销的权限数量
        """
        if skill_name not in self._grants:
            return 0

        count = 0
        for grant in self._grants[skill_name].values():
            if grant.status == AuthorizationStatus.GRANTED:
                grant.status = AuthorizationStatus.REVOKED
                count += 1

        self._save_grants()
        logger.info(f"All permissions revoked for {skill_name}: {count} permissions")
        return count

    # ============================================================================
    # 权限状态管理
    # ============================================================================

    def get_permission_status(
        self,
        skill_name: str,
        permission: Optional[str] = None,
    ) -> Dict[str, Any]:
        """
        获取权限状态

        Args:
            skill_name: Skill 名称
            permission: 可选，指定权限则只返回该权限状态

        Returns:
            权限状态字典
        """
        if skill_name not in self._grants:
            return {"skill_name": skill_name, "permissions": {}}

        skill_grants = self._grants[skill_name]

        if permission:
            grant = skill_grants.get(permission)
            if grant:
                return {
                    "skill_name": skill_name,
                    "permission": permission,
                    "status": grant.status.value,
                    "granted_at": grant.granted_at.isoformat() if grant.granted_at else None,
                    "expires_at": grant.expires_at.isoformat() if grant.expires_at else None,
                }
            return {"skill_name": skill_name, "permission": permission, "status": "unknown"}

        return {
            "skill_name": skill_name,
            "permissions": {
                perm: {
                    "status": grant.status.value,
                    "granted_at": grant.granted_at.isoformat() if grant.granted_at else None,
                }
                for perm, grant in skill_grants.items()
            },
        }

    def list_all_grants(self) -> Dict[str, Dict[str, str]]:
        """
        列出所有授权状态

        Returns:
            {skill_name: {permission: status}} 格式的字典
        """
        return {
            skill_name: {
                perm: grant.status.value
                for perm, grant in grants.items()
            }
            for skill_name, grants in self._grants.items()
        }

    # ============================================================================
    # 持久化
    # ============================================================================

    def _set_grant(
        self,
        skill_name: str,
        permission: str,
        grant: PermissionGrant,
    ) -> None:
        """设置授权记录"""
        if skill_name not in self._grants:
            self._grants[skill_name] = {}
        self._grants[skill_name][permission] = grant

    def _save_grants(self) -> None:
        """保存授权状态到文件"""
        if self._storage_path is None:
            return

        data = {}
        for skill_name, grants in self._grants.items():
            data[skill_name] = {}
            for perm, grant in grants.items():
                data[skill_name][perm] = {
                    "status": grant.status.value,
                    "granted_at": grant.granted_at.isoformat() if grant.granted_at else None,
                    "expires_at": grant.expires_at.isoformat() if grant.expires_at else None,
                    "metadata": grant.metadata,
                }

        self._storage_path.parent.mkdir(parents=True, exist_ok=True)
        self._storage_path.write_text(json.dumps(data, indent=2, ensure_ascii=False))

    def _load_grants(self) -> None:
        """从文件加载授权状态"""
        if self._storage_path is None or not self._storage_path.exists():
            return

        try:
            data = json.loads(self._storage_path.read_text(encoding="utf-8"))

            for skill_name, grants in data.items():
                self._grants[skill_name] = {}
                for perm, grant_data in grants.items():
                    self._grants[skill_name][perm] = PermissionGrant(
                        permission=perm,
                        status=AuthorizationStatus(grant_data["status"]),
                        granted_at=datetime.fromisoformat(grant_data["granted_at"])
                            if grant_data.get("granted_at") else None,
                        expires_at=datetime.fromisoformat(grant_data["expires_at"])
                            if grant_data.get("expires_at") else None,
                        metadata=grant_data.get("metadata", {}),
                    )

            logger.info(f"Loaded permission grants from {self._storage_path}")

        except Exception as e:
            logger.error(f"Failed to load permission grants: {e}")

    # ============================================================================
    # 授权回调实现
    # ============================================================================


async def terminal_authorization_callback(requests: List[PermissionRequest]) -> List[str]:
    """
    终端交互式授权回调

    在终端中显示权限请求，让用户选择授权。

    Args:
        requests: 权限请求列表

    Returns:
        用户授予的权限字符串列表
    """
    if not requests:
        return []

    print("\n" + "=" * 60)
    print("           权限授权请求 / Permission Request")
    print("=" * 60)
    print(f"Skill: {requests[0].skill_name}")
    print("-" * 60)

    # 显示操作目的（如果有）
    if requests[0].purpose:
        print(f"操作目的: {requests[0].purpose}")
        print("-" * 60)

    print("请求的权限:")
    print()

    for i, req in enumerate(requests, 1):
        required_mark = "[必需/Required]" if req.required else "[可选/Optional]"
        print(f"  {i}. {required_mark}")
        print(f"     类型: {req.permission_type}")
        print(f"     范围: {req.scope}")
        if req.reason:
            print(f"     原因: {req.reason}")
        print()

    print("-" * 60)
    print("请输入要授权的权限编号（用逗号分隔）")
    print("输入 'all' 全部授权，'none' 全部拒绝，'required' 仅授权必需项")
    print()

    try:
        user_input = input("> ").strip().lower()
    except EOFError:
        # 非交互模式
        return []

    if user_input == "none":
        return []
    elif user_input == "all":
        return [req.to_permission_string() for req in requests]
    elif user_input == "required":
        return [req.to_permission_string() for req in requests if req.required]
    else:
        # 解析编号
        try:
            indices = [int(x.strip()) - 1 for x in user_input.split(",")]
            return [
                requests[i].to_permission_string()
                for i in indices
                if 0 <= i < len(requests)
            ]
        except ValueError:
            print("输入格式错误，默认拒绝所有权限")
            return []


async def auto_grant_callback(requests: List[PermissionRequest]) -> List[str]:
    """
    自动授权回调（仅用于测试）

    自动授予所有请求的权限。

    Args:
        requests: 权限请求列表

    Returns:
        所有权限字符串列表
    """
    return [req.to_permission_string() for req in requests]


async def deny_all_callback(requests: List[PermissionRequest]) -> List[str]:
    """
    拒绝所有回调

    拒绝所有权限请求（只授予必需权限）。

    Args:
        requests: 权限请求列表

    Returns:
        仅必需权限的字符串列表
    """
    return [req.to_permission_string() for req in requests if req.required]


# 全局变量：用于取消 tkinter 对话框
_tkinter_cancel_flag = False


class TaskCancelledException(Exception):
    """任务被取消异常"""
    pass


def set_tkinter_cancel_flag(cancel: bool = True) -> None:
    """设置 tkinter 对话框取消标志（线程安全）"""
    global _tkinter_cancel_flag
    _tkinter_cancel_flag = cancel
    # 注意：不要在这里直接销毁窗口，因为可能从其他线程调用
    # 窗口会通过 after() 定期检查此标志并自行关闭


def check_tkinter_cancel_flag() -> bool:
    """检查取消标志"""
    return _tkinter_cancel_flag


async def tkinter_authorization_callback(requests: List[PermissionRequest]) -> List[str]:
    """
    Tkinter 弹窗授权回调

    使用 tkinter 弹窗显示权限请求，让用户选择授权。
    适用于非命令行环境（如全局快捷键触发）。

    Args:
        requests: 权限请求列表

    Returns:
        用户授予的权限字符串列表
    """
    global _tkinter_cancel_flag

    if not requests:
        return []

    # 先检查取消标志，如果已取消则抛出异常中断任务
    if _tkinter_cancel_flag:
        raise TaskCancelledException("用户取消了任务")

    try:
        import tkinter as tk
        from tkinter import messagebox
    except ImportError:
        logger.error("tkinter not available for permission dialog")
        return []

    # 构建权限信息文本
    skill_name = requests[0].skill_name
    purpose = requests[0].purpose if requests[0].purpose else "执行操作"

    msg_lines = [
        f"Skill: {skill_name}",
        f"操作: {purpose}",
        "",
        "请求的权限:",
    ]

    for i, req in enumerate(requests, 1):
        required_mark = "[必需]" if req.required else "[可选]"
        msg_lines.append(f"  {i}. {required_mark} {req.permission_type}:{req.scope}")
        if req.reason:
            msg_lines.append(f"      原因: {req.reason}")

    msg_lines.append("")
    msg_lines.append("是否授权？")

    message = "\n".join(msg_lines)

    # 使用简单的 messagebox（更稳定）
    result = [None]

    def show_dialog():
        # 再次检查取消标志
        if _tkinter_cancel_flag:
            return

        root = tk.Tk()
        root.withdraw()
        root.attributes('-topmost', True)

        try:
            answer = messagebox.askyesno(
                "权限授权请求",
                message,
                parent=root
            )
            result[0] = answer
        except Exception:
            pass
        finally:
            try:
                root.destroy()
            except Exception:
                pass

    show_dialog()

    # 检查取消标志
    if _tkinter_cancel_flag:
        return []

    if result[0] is True:
        return [req.to_permission_string() for req in requests]
    else:
        return []
