"""
陪练场景准备服务 - Python 原生实现

功能：
- 根据课程ID获取知识点
- 调用 AI 生成陪练场景配置
- 解析并返回结构化场景数据

提供稳定可靠的陪练场景提取能力。
"""

import logging
from dataclasses import dataclass, field
from typing import Any, Dict, List, Optional

from sqlalchemy import text
from sqlalchemy.ext.asyncio import AsyncSession

from app.core.exceptions import ExternalServiceError

from .ai_service import AIService, AIResponse
from .llm_json_parser import parse_with_fallback, clean_llm_output
from .prompts.practice_scene_prompts import (
    SYSTEM_PROMPT,
    USER_PROMPT,
    PRACTICE_SCENE_SCHEMA,
    DEFAULT_SCENE_TYPE,
    DEFAULT_DIFFICULTY,
)

logger = logging.getLogger(__name__)


# ==================== 数据结构 ====================

@dataclass
class PracticeScene:
    """陪练场景数据结构"""
    name: str
    description: str
    background: str
    ai_role: str
    objectives: List[str]
    keywords: List[str]
    type: str = DEFAULT_SCENE_TYPE
    difficulty: str = DEFAULT_DIFFICULTY


@dataclass
class PracticeSceneResult:
    """陪练场景生成结果"""
    success: bool
    scene: Optional[PracticeScene] = None
    raw_response: Dict[str, Any] = field(default_factory=dict)
    ai_provider: str = ""
    ai_model: str = ""
    ai_tokens: int = 0
    ai_latency_ms: int = 0
    knowledge_points_count: int = 0
    error: str = ""


# ==================== 服务类 ====================

class PracticeSceneService:
    """
    陪练场景准备服务
    
    使用 Python 原生实现。
    
    使用示例：
    ```python
    service = PracticeSceneService()
    result = await service.prepare_practice_knowledge(
        db=db_session,
        course_id=1
    )
    if result.success:
        print(result.scene.name)
        print(result.scene.objectives)
    ```
    """
    
    def __init__(self):
        """初始化服务"""
        self.ai_service = AIService(module_code="practice_scene")
    
    async def prepare_practice_knowledge(
        self,
        db: AsyncSession,
        course_id: int
    ) -> PracticeSceneResult:
        """
        准备陪练所需的知识内容并生成场景
        
        陪练知识准备的 Python 实现。
        
        Args:
            db: 数据库会话（支持多租户，由调用方传入对应租户的数据库连接）
            course_id: 课程ID
            
        Returns:
            PracticeSceneResult: 包含场景配置和元信息的结果对象
        """
        try:
            logger.info(f"开始陪练知识准备 - course_id: {course_id}")
            
            # 1. 查询知识点
            knowledge_points = await self._fetch_knowledge_points(db, course_id)
            
            if not knowledge_points:
                logger.warning(f"课程没有知识点 - course_id: {course_id}")
                return PracticeSceneResult(
                    success=False,
                    error=f"课程 {course_id} 没有可用的知识点"
                )
            
            logger.info(f"获取到 {len(knowledge_points)} 个知识点 - course_id: {course_id}")
            
            # 2. 格式化知识点为文本
            knowledge_text = self._format_knowledge_points(knowledge_points)
            
            # 3. 调用 AI 生成场景
            ai_response = await self._call_ai_generation(knowledge_text)
            
            logger.info(
                f"AI 生成完成 - provider: {ai_response.provider}, "
                f"tokens: {ai_response.total_tokens}, latency: {ai_response.latency_ms}ms"
            )
            
            # 4. 解析 JSON 结果
            scene_data = self._parse_scene_response(ai_response.content)
            
            if not scene_data:
                logger.error(f"场景解析失败 - course_id: {course_id}")
                return PracticeSceneResult(
                    success=False,
                    raw_response={"ai_output": ai_response.content},
                    ai_provider=ai_response.provider,
                    ai_model=ai_response.model,
                    ai_tokens=ai_response.total_tokens,
                    ai_latency_ms=ai_response.latency_ms,
                    knowledge_points_count=len(knowledge_points),
                    error="AI 输出解析失败"
                )
            
            # 5. 构建场景对象
            scene = self._build_scene_object(scene_data)
            
            logger.info(
                f"陪练场景生成成功 - course_id: {course_id}, "
                f"scene_name: {scene.name}, type: {scene.type}"
            )
            
            return PracticeSceneResult(
                success=True,
                scene=scene,
                raw_response=scene_data,
                ai_provider=ai_response.provider,
                ai_model=ai_response.model,
                ai_tokens=ai_response.total_tokens,
                ai_latency_ms=ai_response.latency_ms,
                knowledge_points_count=len(knowledge_points)
            )
            
        except Exception as e:
            logger.error(
                f"陪练知识准备失败 - course_id: {course_id}, error: {e}",
                exc_info=True
            )
            return PracticeSceneResult(
                success=False,
                error=str(e)
            )
    
    async def _fetch_knowledge_points(
        self,
        db: AsyncSession,
        course_id: int
    ) -> List[Dict[str, Any]]:
        """
        从数据库获取课程知识点
        
        获取课程知识点
        """
        # 知识点查询 SQL：
        # SELECT kp.name, kp.description 
        # FROM knowledge_points kp 
        # INNER JOIN course_materials cm ON kp.material_id = cm.id 
        # WHERE kp.course_id = {course_id} 
        # AND kp.is_deleted = 0 
        # AND cm.is_deleted = 0 
        # ORDER BY kp.id;
        
        sql = text("""
            SELECT kp.name, kp.description 
            FROM knowledge_points kp 
            INNER JOIN course_materials cm ON kp.material_id = cm.id 
            WHERE kp.course_id = :course_id 
            AND kp.is_deleted = 0 
            AND cm.is_deleted = 0 
            ORDER BY kp.id
        """)
        
        try:
            result = await db.execute(sql, {"course_id": course_id})
            rows = result.fetchall()
            
            knowledge_points = []
            for row in rows:
                knowledge_points.append({
                    "name": row[0],
                    "description": row[1] or ""
                })
            
            return knowledge_points
            
        except Exception as e:
            logger.error(f"查询知识点失败: {e}")
            raise ExternalServiceError(f"数据库查询失败: {e}")
    
    def _format_knowledge_points(self, knowledge_points: List[Dict[str, Any]]) -> str:
        """
        将知识点列表格式化为文本
        
        Args:
            knowledge_points: 知识点列表
            
        Returns:
            格式化后的文本
        """
        lines = []
        for i, kp in enumerate(knowledge_points, 1):
            name = kp.get("name", "")
            description = kp.get("description", "")
            
            if description:
                lines.append(f"{i}. {name}\n   {description}")
            else:
                lines.append(f"{i}. {name}")
        
        return "\n\n".join(lines)
    
    async def _call_ai_generation(self, knowledge_text: str) -> AIResponse:
        """
        调用 AI 生成陪练场景
        
        Args:
            knowledge_text: 格式化后的知识点文本
            
        Returns:
            AI 响应对象
        """
        # 构建用户消息
        user_message = USER_PROMPT.format(knowledge_points=knowledge_text)
        
        messages = [
            {"role": "system", "content": SYSTEM_PROMPT},
            {"role": "user", "content": user_message}
        ]
        
        # 调用 AI（自动降级：4sapi.com → OpenRouter）
        response = await self.ai_service.chat(
            messages=messages,
            temperature=0.7,  # 适中的创意性
            prompt_name="practice_scene_generation"
        )
        
        return response
    
    def _parse_scene_response(self, ai_output: str) -> Optional[Dict[str, Any]]:
        """
        解析 AI 输出的场景 JSON
        
        使用 LLM JSON Parser 进行多层兜底解析
        
        Args:
            ai_output: AI 原始输出
            
        Returns:
            解析后的字典，失败返回 None
        """
        # 先清洗输出
        cleaned_output, rules = clean_llm_output(ai_output)
        if rules:
            logger.debug(f"AI 输出已清洗: {rules}")
        
        # 使用带 Schema 校验的解析
        result = parse_with_fallback(
            cleaned_output,
            schema=PRACTICE_SCENE_SCHEMA,
            default=None,
            validate_schema=True,
            on_error="none"
        )
        
        return result
    
    def _build_scene_object(self, scene_data: Dict[str, Any]) -> PracticeScene:
        """
        从解析的字典构建场景对象
        
        Args:
            scene_data: 解析后的场景数据
            
        Returns:
            PracticeScene 对象
        """
        # 提取 scene 字段（JSON 格式为 {"scene": {...}}）
        scene = scene_data.get("scene", scene_data)
        
        return PracticeScene(
            name=scene.get("name", "陪练场景"),
            description=scene.get("description", ""),
            background=scene.get("background", ""),
            ai_role=scene.get("ai_role", "AI扮演客户"),
            objectives=scene.get("objectives", []),
            keywords=scene.get("keywords", []),
            type=scene.get("type", DEFAULT_SCENE_TYPE),
            difficulty=scene.get("difficulty", DEFAULT_DIFFICULTY)
        )
    
    def scene_to_dict(self, scene: PracticeScene) -> Dict[str, Any]:
        """
        将场景对象转换为字典
        
        便于 API 响应序列化
        
        Args:
            scene: PracticeScene 对象
            
        Returns:
            字典格式的场景数据
        """
        return {
            "scene": {
                "name": scene.name,
                "description": scene.description,
                "background": scene.background,
                "ai_role": scene.ai_role,
                "objectives": scene.objectives,
                "keywords": scene.keywords,
                "type": scene.type,
                "difficulty": scene.difficulty
            }
        }


# ==================== 全局实例 ====================

practice_scene_service = PracticeSceneService()


# ==================== 便捷函数 ====================

async def prepare_practice_knowledge(
    db: AsyncSession,
    course_id: int
) -> PracticeSceneResult:
    """
    准备陪练所需的知识内容（便捷函数）
    
    Args:
        db: 数据库会话
        course_id: 课程ID
        
    Returns:
        PracticeSceneResult 结果对象
    """
    return await practice_scene_service.prepare_practice_knowledge(db, course_id)














