"""
Coze AI 客户端管理
负责管理 Coze API 的认证和客户端实例
"""
from functools import lru_cache
from typing import Optional, Dict, Any
import logging
from pathlib import Path

from cozepy import Coze, TokenAuth, JWTAuth, COZE_CN_BASE_URL

from app.core.config import get_settings

logger = logging.getLogger(__name__)


class CozeAuthManager:
    """Coze 认证管理器"""

    def __init__(self):
        self.settings = get_settings()
        self._client: Optional[Coze] = None

    def _create_pat_auth(self) -> TokenAuth:
        """创建个人访问令牌认证"""
        if not self.settings.COZE_API_TOKEN:
            raise ValueError("COZE_API_TOKEN 未配置")

        return TokenAuth(token=self.settings.COZE_API_TOKEN)

    def _create_oauth_auth(self) -> JWTAuth:
        """创建 OAuth 认证"""
        if not all(
            [
                self.settings.COZE_OAUTH_CLIENT_ID,
                self.settings.COZE_OAUTH_PUBLIC_KEY_ID,
                self.settings.COZE_OAUTH_PRIVATE_KEY_PATH,
            ]
        ):
            raise ValueError("OAuth 配置不完整")

        # 读取私钥
        private_key_path = Path(self.settings.COZE_OAUTH_PRIVATE_KEY_PATH)
        if not private_key_path.exists():
            raise FileNotFoundError(f"私钥文件不存在: {private_key_path}")

        with open(private_key_path, "r") as f:
            private_key = f.read()

        try:
            return JWTAuth(
                client_id=self.settings.COZE_OAUTH_CLIENT_ID,
                private_key=private_key,
                public_key_id=self.settings.COZE_OAUTH_PUBLIC_KEY_ID,
                base_url=self.settings.COZE_API_BASE or COZE_CN_BASE_URL,  # 使用中国区API
            )
        except Exception as e:
            logger.error(f"创建 OAuth 认证失败: {e}")
            raise

    def get_client(self, force_new: bool = False) -> Coze:
        """
        获取 Coze 客户端实例
        
        Args:
            force_new: 是否强制创建新客户端（用于长时间运行的请求，避免token过期）
        
        认证优先级：
        1. OAuth（推荐）：配置完整时使用，自动刷新token
        2. PAT：仅当OAuth未配置时使用（注意：PAT会过期）
        """
        if self._client is not None and not force_new:
            return self._client

        auth = None
        auth_type = None
        
        # 检查 OAuth 配置是否完整
        oauth_configured = all([
            self.settings.COZE_OAUTH_CLIENT_ID,
            self.settings.COZE_OAUTH_PUBLIC_KEY_ID,
            self.settings.COZE_OAUTH_PRIVATE_KEY_PATH,
        ])
        
        if oauth_configured:
            # OAuth 配置完整，必须使用 OAuth（不fallback到PAT）
            try:
                auth = self._create_oauth_auth()
                auth_type = "OAuth"
                logger.info("使用 OAuth 认证")
            except Exception as e:
                # OAuth 配置完整但创建失败，直接抛出异常（不fallback到可能过期的PAT）
                logger.error(f"OAuth 认证创建失败: {e}")
                raise ValueError(f"OAuth 认证失败，请检查私钥文件和配置: {e}")
        else:
            # OAuth 未配置，使用 PAT
            if self.settings.COZE_API_TOKEN:
                auth = self._create_pat_auth()
                auth_type = "PAT"
                logger.warning("使用 PAT 认证（注意：PAT会过期，建议配置OAuth）")
            else:
                raise ValueError("Coze 认证未配置：需要配置 OAuth 或 PAT Token")

        # 创建客户端
        client = Coze(
            auth=auth, base_url=self.settings.COZE_API_BASE or COZE_CN_BASE_URL
        )
        
        logger.debug(f"Coze客户端创建成功，认证方式: {auth_type}, force_new: {force_new}")
        
        # 只有非强制创建时才缓存
        if not force_new:
            self._client = client

        return client

    def reset(self):
        """重置客户端实例"""
        self._client = None
    
    def get_oauth_token(self) -> str:
        """
        获取OAuth JWT Token用于前端直连
        
        Returns:
            JWT token字符串
        """
        if not all([
            self.settings.COZE_OAUTH_CLIENT_ID,
            self.settings.COZE_OAUTH_PUBLIC_KEY_ID,
            self.settings.COZE_OAUTH_PRIVATE_KEY_PATH,
        ]):
            raise ValueError("OAuth 配置不完整")
        
        # 读取私钥
        private_key_path = Path(self.settings.COZE_OAUTH_PRIVATE_KEY_PATH)
        if not private_key_path.exists():
            raise FileNotFoundError(f"私钥文件不存在: {private_key_path}")
        
        with open(private_key_path, "r") as f:
            private_key = f.read()
        
        # 创建JWTAuth实例（必须指定中国区base_url）
        jwt_auth = JWTAuth(
            client_id=self.settings.COZE_OAUTH_CLIENT_ID,
            private_key=private_key,
            public_key_id=self.settings.COZE_OAUTH_PUBLIC_KEY_ID,
            base_url=self.settings.COZE_API_BASE or COZE_CN_BASE_URL,  # 使用中国区API
        )
        
        # 获取token（JWTAuth内部会自动生成）
        # JWTAuth.token属性返回已签名的JWT
        return jwt_auth.token


@lru_cache()
def get_auth_manager() -> CozeAuthManager:
    """获取认证管理器单例"""
    return CozeAuthManager()


def get_coze_client(force_new: bool = False) -> Coze:
    """
    获取 Coze 客户端
    
    Args:
        force_new: 是否强制创建新客户端（用于工作流等长时间运行的请求）
    """
    return get_auth_manager().get_client(force_new=force_new)


def get_workspace_id() -> str:
    """获取工作空间 ID"""
    settings = get_settings()
    if not settings.COZE_WORKSPACE_ID:
        raise ValueError("COZE_WORKSPACE_ID 未配置")
    return settings.COZE_WORKSPACE_ID


def get_bot_config(session_type: str) -> Dict[str, Any]:
    """
    根据会话类型获取 Bot 配置

    Args:
        session_type: 会话类型 (course_chat 或 training)

    Returns:
        包含 bot_id 等配置的字典
    """
    settings = get_settings()

    if session_type == "course_chat":
        bot_id = settings.COZE_CHAT_BOT_ID
        if not bot_id:
            raise ValueError("COZE_CHAT_BOT_ID 未配置")
    elif session_type == "training":
        bot_id = settings.COZE_TRAINING_BOT_ID
        if not bot_id:
            raise ValueError("COZE_TRAINING_BOT_ID 未配置")
    else:
        raise ValueError(f"不支持的会话类型: {session_type}")

    return {"bot_id": bot_id, "workspace_id": settings.COZE_WORKSPACE_ID}
