"""备课工作流服务（4 步编排 + 状态机）.

状态机（02-architecture-design.md §2.4）：
  Pending → Analyzing → Analyzed/AnalyzeFailed
  → Recommending → Recommended
  → Generating → Generated/Regenerating/GenerationFailed
  → PendingReview → Confirmed/Modified/Rejected/Expired
  → Persisting → Persisted/PersistFailed

P5 实现：FastAPI BackgroundTasks + Redis 状态存储
P6+ 评估：Temporal 工作流引擎

4 步编排：
  Step 1: 分析学情（调 data-ana.GetClassPerformance + GetStudentWeakness）
  Step 2: 推荐知识点（调 content.GetPrerequisites + GetLearningPath）
  Step 3: 生成题目（LLM 生成 + 评估三道防线，最多重试 3 次）
  Step 4: 教师审核（pending_review → confirm → content.CreateQuestions 入库）
"""

import asyncio
from typing import Any

import structlog

from ..clients.content_client import ContentClient, QuestionInput
from ..clients.data_ana_client import DataAnaClient
from ..errors import AIError, AIWorkflowStateInvalidError
from ..models.question import GeneratedQuestionData
from ..prompt_service import PromptTemplateService
from ..providers import ProviderFailoverChain
from ..services.evaluation import QualityGate
from .state_store import WorkflowState, WorkflowStateStore

logger = structlog.get_logger()

# 状态常量（简化 proto WorkflowStatus，内部用）
STATUS_PENDING = "pending"
STATUS_ANALYZING = "analyzing"
STATUS_GENERATING = "generating"
STATUS_PENDING_REVIEW = "pending_review"
STATUS_PERSISTED = "persisted"
STATUS_FAILED = "failed"

# 生成重试上限
MAX_GENERATE_RETRIES = 3

# 预估完成时间（秒）
ESTIMATED_COMPLETION_SECONDS = 60


class LessonPlanWorkflowService:
    """备课工作流服务.

    P5 使用 asyncio.create_task 在后台执行工作流。
    P6+ 评估迁移到 Temporal。
    """

    def __init__(
        self,
        state_store: WorkflowStateStore,
        failover_chain: ProviderFailoverChain,
        prompt_service: PromptTemplateService | None = None,
        quality_gate: QualityGate | None = None,
        content_client: ContentClient | None = None,
        data_ana_client: DataAnaClient | None = None,
        default_model: str = "gpt-4o-mini",
    ) -> None:
        self._store = state_store
        self._chain = failover_chain
        self._prompts = prompt_service
        self._quality_gate = quality_gate or QualityGate()
        self._content_client = content_client
        self._data_ana_client = data_ana_client
        self._default_model = default_model
        self._background_tasks: dict[str, asyncio.Task[Any]] = {}

    async def start(
        self,
        user_id: str,
        school_id: str,
        class_id: str,
        subject_id: str,
        topic: str,
        target_difficulty: str = "medium",
        question_count: int = 5,
        request_id: str = "",
    ) -> WorkflowState:
        """启动备课工作流.

        创建工作流状态，立即返回，后台异步执行 4 步编排。

        Args:
            user_id: 用户 ID
            school_id: 学校 ID
            class_id: 班级 ID
            subject_id: 学科 ID
            topic: 备课主题
            target_difficulty: 目标难度
            question_count: 题目数量
            request_id: 请求 ID

        Returns:
            WorkflowState（含 workflow_id，status=pending）
        """
        state = WorkflowState(
            user_id=user_id,
            school_id=school_id,
            class_id=class_id,
            subject_id=subject_id,
            topic=topic,
            target_difficulty=target_difficulty,
            question_count=question_count,
            status=STATUS_PENDING,
            request_id=request_id,
        )
        await self._store.create(state)

        # 启动后台任务执行工作流
        task = asyncio.create_task(self._run_workflow(state.workflow_id))
        self._background_tasks[state.workflow_id] = task

        logger.info(
            "workflow_started",
            workflow_id=state.workflow_id,
            user_id=user_id,
            topic=topic,
        )
        return state

    async def get_status(self, workflow_id: str) -> WorkflowState:
        """查询工作流状态.

        Args:
            workflow_id: 工作流 ID

        Returns:
            WorkflowState

        Raises:
            AIWorkflowNotFoundError: 工作流不存在
        """
        return await self._store.get(workflow_id)

    async def confirm(
        self,
        workflow_id: str,
        modifications: dict[str, str] | None = None,
    ) -> dict[str, Any]:
        """教师确认备课结果入库.

        Args:
            workflow_id: 工作流 ID
            modifications: 教师修改（question_index → 修改后内容）

        Returns:
            {"success": bool, "persisted_question_ids": list[str]}

        Raises:
            AIWorkflowNotFoundError: 工作流不存在
            AIWorkflowStateInvalidError: 工作流状态不允许确认
        """
        state = await self._store.get(workflow_id)

        if state.status != STATUS_PENDING_REVIEW:
            raise AIWorkflowStateInvalidError(
                workflow_id,
                state.status,
                "confirm",
            )

        # 应用教师修改
        questions = state.questions
        if modifications:
            for idx_str, modified_content in modifications.items():
                try:
                    idx = int(idx_str)
                    if 0 <= idx < len(questions):
                        questions[idx].question = modified_content
                except (ValueError, IndexError):
                    logger.warning(
                        "workflow_confirm_invalid_modification",
                        workflow_id=workflow_id,
                        index=idx_str,
                    )

        # 调 content.CreateQuestions 入库
        if self._content_client is None:
            logger.warning(
                "workflow_confirm_no_content_client",
                workflow_id=workflow_id,
            )
            return {
                "success": False,
                "persisted_question_ids": [],
                "error": "content client not configured",
            }

        question_inputs = [
            QuestionInput(
                question=q.question,
                answer=q.answer,
                explanation=q.explanation,
                question_type=q.question_type,
                difficulty=q.difficulty,
                knowledge_point_ids=q.knowledge_point_ids,
            )
            for q in questions
        ]

        try:
            created = await self._content_client.create_questions(
                question_inputs,
                user_id=state.user_id,
            )
            persisted_ids = [c.id for c in created]

            await self._store.update(
                workflow_id,
                status=STATUS_PERSISTED,
            )

            logger.info(
                "workflow_confirmed",
                workflow_id=workflow_id,
                persisted_count=len(persisted_ids),
            )

            return {
                "success": True,
                "persisted_question_ids": persisted_ids,
            }
        except AIError as exc:
            await self._store.update(
                workflow_id,
                status=STATUS_FAILED,
                error=str(exc),
            )
            return {
                "success": False,
                "persisted_question_ids": [],
                "error": str(exc),
            }

    async def _run_workflow(self, workflow_id: str) -> None:
        """执行 4 步编排（后台任务）.

        Step 1: 分析学情
        Step 2: 推荐知识点
        Step 3: 生成题目
        Step 4: 设置为待审核
        """
        try:
            state = await self._store.get(workflow_id)

            # Step 1: 分析学情
            await self._store.update(
                workflow_id,
                status=STATUS_ANALYZING,
            )
            analysis = await self._step1_analyze(state)
            state = await self._store.update(
                workflow_id,
                analysis=analysis,
            )

            # Step 2: 推荐知识点
            knowledge_points = await self._step2_recommend(state)

            # Step 3: 生成题目
            await self._store.update(
                workflow_id,
                status=STATUS_GENERATING,
            )
            questions = await self._step3_generate(state, knowledge_points)

            # Step 4: 设置为待审核
            await self._store.update(
                workflow_id,
                status=STATUS_PENDING_REVIEW,
                questions=questions,
            )

            logger.info(
                "workflow_completed_pending_review",
                workflow_id=workflow_id,
                question_count=len(questions),
            )

        except Exception as exc:  # noqa: BLE001
            logger.error(
                "workflow_failed",
                workflow_id=workflow_id,
                error=str(exc),
            )
            await self._store.update(
                workflow_id,
                status=STATUS_FAILED,
                error=str(exc),
            )
        finally:
            self._background_tasks.pop(workflow_id, None)

    async def _step1_analyze(self, state: WorkflowState) -> dict[str, Any]:
        """Step 1: 分析学情.

        调 data-ana 查询班级学情 + 学生薄弱点。
        全并行模式：data-ana 不可用时降级（返回空分析）。
        """
        analysis: dict[str, Any] = {}

        if self._data_ana_client is not None:
            try:
                performance = await self._data_ana_client.get_class_performance(
                    class_id=state.class_id,
                    subject_id=state.subject_id,
                )
                analysis["class_performance"] = {
                    "average_score": performance.average_score,
                    "pass_rate": performance.pass_rate,
                    "student_count": len(performance.scores),
                }
                analysis["weak_students"] = [
                    {"student_id": s.student_id, "score": s.score}
                    for s in performance.scores
                    if s.score < 60
                ]
            except Exception as exc:  # noqa: BLE001
                logger.warning(
                    "workflow_step1_analyze_degraded",
                    workflow_id=state.workflow_id,
                    error=str(exc),
                )
                analysis["degraded"] = True
                analysis["degraded_reason"] = f"data-ana unavailable: {exc}"
        else:
            analysis["degraded"] = True
            analysis["degraded_reason"] = "data-ana client not configured"

        logger.info(
            "workflow_step1_completed",
            workflow_id=state.workflow_id,
            degraded=analysis.get("degraded", False),
        )
        return analysis

    async def _step2_recommend(
        self,
        state: WorkflowState,
    ) -> list[dict[str, str]]:
        """Step 2: 推荐知识点.

        调 content 查询知识点前置依赖 + 学习路径。
        全并行模式：content 不可用时降级（基于 topic 推导）。
        """
        knowledge_points: list[dict[str, str]] = []

        if self._content_client is not None:
            try:
                # 查询学习路径
                learning_path = await self._content_client.get_learning_path(
                    student_id=state.user_id,
                    subject_id=state.subject_id,
                )
                knowledge_points = [
                    {"id": kp.id, "title": kp.title}
                    for kp in learning_path
                ]
            except Exception as exc:  # noqa: BLE001
                logger.warning(
                    "workflow_step2_recommend_degraded",
                    workflow_id=state.workflow_id,
                    error=str(exc),
                )

        # 降级：基于 topic 推导知识点
        if not knowledge_points:
            knowledge_points = [
                {"id": "kp_default_1", "title": f"{state.topic} - 基础概念"},
                {"id": "kp_default_2", "title": f"{state.topic} - 进阶应用"},
                {"id": "kp_default_3", "title": f"{state.topic} - 综合题"},
            ]

        logger.info(
            "workflow_step2_completed",
            workflow_id=state.workflow_id,
            knowledge_point_count=len(knowledge_points),
        )
        return knowledge_points

    async def _step3_generate(
        self,
        state: WorkflowState,
        knowledge_points: list[dict[str, str]],
    ) -> list[GeneratedQuestionData]:
        """Step 3: 生成题目.

        使用 LLM 生成题目 + 评估三道防线。
        评估未通过时重试（最多 MAX_GENERATE_RETRIES 次）。
        """
        questions: list[GeneratedQuestionData] = []
        kp_ids = [kp["id"] for kp in knowledge_points]

        for i in range(state.question_count):
            question = await self._generate_single_question(
                state,
                kp_ids,
                question_index=i,
            )
            questions.append(question)

        logger.info(
            "workflow_step3_completed",
            workflow_id=state.workflow_id,
            question_count=len(questions),
        )
        return questions

    async def _generate_single_question(
        self,
        state: WorkflowState,
        kp_ids: list[str],
        question_index: int = 0,
    ) -> GeneratedQuestionData:
        """生成单道题目（含重试）."""
        prompt = self._render_generate_prompt(state, kp_ids, question_index)
        messages = [
            {"role": "system", "content": "你是一个专业的教育题目生成助手。"},
            {"role": "user", "content": prompt},
        ]

        for attempt in range(MAX_GENERATE_RETRIES):
            try:
                response = await self._chain.chat(
                    messages,
                    self._default_model,
                    0.7,
                )

                # 评估三道防线
                evaluation = await self._quality_gate.evaluate(
                    llm_output=response.content,
                    expected_difficulty=state.target_difficulty,
                    expected_question_type="short_answer",
                    subject=state.subject_id,
                )

                parsed = (
                    evaluation.rule_result.parsed
                    if evaluation.rule_result
                    else None
                )

                if parsed and evaluation.passed:
                    return GeneratedQuestionData(
                        question=str(parsed.get("question", "")),
                        answer=str(parsed.get("answer", "")),
                        explanation=str(parsed.get("explanation", "")),
                        question_type=str(
                            parsed.get("question_type", "short_answer"),
                        ),
                        difficulty=str(
                            parsed.get("difficulty", state.target_difficulty),
                        ),
                        knowledge_point_ids=list(
                            parsed.get("knowledge_point_ids", kp_ids),
                        ),
                        evaluation_score=evaluation.score,
                    )

                # 评估未通过，重试
                logger.warning(
                    "workflow_generate_retry",
                    workflow_id=state.workflow_id,
                    attempt=attempt + 1,
                    score=evaluation.score,
                )

            except Exception as exc:  # noqa: BLE001
                logger.warning(
                    "workflow_generate_error_retry",
                    workflow_id=state.workflow_id,
                    attempt=attempt + 1,
                    error=str(exc),
                )

        # 所有重试失败，返回降级题目
        logger.warning(
            "workflow_generate_all_retries_failed",
            workflow_id=state.workflow_id,
            question_index=question_index,
        )
        return GeneratedQuestionData(
            question=f"[degraded] 生成失败，请手动添加题目：{state.topic}",
            answer="",
            explanation="题目生成失败，已达到最大重试次数",
            question_type="short_answer",
            difficulty=state.target_difficulty,
            knowledge_point_ids=kp_ids,
            evaluation_score=0.0,
            degraded=True,
            degraded_reason="max retries exceeded",
        )

    def _render_generate_prompt(
        self,
        state: WorkflowState,
        kp_ids: list[str],
        question_index: int = 0,
    ) -> str:
        """渲染题目生成 prompt."""
        if self._prompts is not None:
            try:
                return self._prompts.render(
                    "lesson_plan_generate",
                    {
                        "subject": state.subject_id,
                        "topic": state.topic,
                        "difficulty": state.target_difficulty,
                        "knowledge_points": kp_ids,
                        "knowledge_point_ids": kp_ids,
                        "question_index": question_index,
                        "analysis": state.analysis,
                    },
                )
            except Exception:  # noqa: BLE001
                pass

        # 降级 prompt
        return (
            f"请为 {state.subject_id} 学科生成一道题目。\n"
            f"主题：{state.topic}\n"
            f"难度：{state.target_difficulty}\n"
            f"知识点：{', '.join(kp_ids)}\n"
            "请按 JSON 格式输出："
            '{"question":"...","answer":"...","explanation":"..."}'
        )
