"""工作流状态存储（Redis 持久化，24h TTL）.

存储备课工作流的状态和中间结果，支持：
  - create: 创建工作流
  - get: 查询工作流状态
  - update: 更新工作流状态
  - delete: 删除工作流

Redis key 格式：workflow:{workflow_id}
TTL：24h（86400s，可配置）

全并行模式：Redis 不可用时降级到内存存储（仅单实例有效）。
"""

import json
import uuid
from dataclasses import asdict, dataclass, field
from datetime import UTC, datetime
from typing import Any

import structlog
from redis.asyncio import Redis
from redis.exceptions import RedisError

from ..errors import AIWorkflowNotFoundError
from ..models.question import GeneratedQuestionData
from ..models.workflow import WorkflowStatus

logger = structlog.get_logger()

# Redis key 前缀
WORKFLOW_KEY_PREFIX = "workflow"

# 默认 TTL（24h）
DEFAULT_TTL_SECONDS = 86400


@dataclass
class WorkflowState:
    """工作流状态."""

    workflow_id: str = ""
    user_id: str = ""
    school_id: str = ""
    class_id: str = ""
    subject_id: str = ""
    topic: str = ""
    target_difficulty: str = "medium"
    question_count: int = 5
    status: WorkflowStatus = "pending"
    questions: list[GeneratedQuestionData] = field(default_factory=list)
    analysis: dict[str, Any] = field(default_factory=dict)
    error: str | None = None
    created_at: int = 0
    updated_at: int = 0
    request_id: str = ""

    def __post_init__(self) -> None:
        if not self.workflow_id:
            self.workflow_id = str(uuid.uuid4())
        now = int(datetime.now(UTC).timestamp() * 1000)
        if not self.created_at:
            self.created_at = now
        if not self.updated_at:
            self.updated_at = now

    def to_dict(self) -> dict[str, Any]:
        """序列化为 dict（用于 Redis JSON 存储）."""
        data = asdict(self)
        # GeneratedQuestionData 转换为 dict
        data["questions"] = [
            q.model_dump() if hasattr(q, "model_dump") else asdict(q)
            for q in self.questions
        ]
        return data

    @classmethod
    def from_dict(cls, data: dict[str, Any]) -> "WorkflowState":
        """从 dict 反序列化."""
        questions_data = data.get("questions", [])
        questions = [
            GeneratedQuestionData(**q) if isinstance(q, dict) else q
            for q in questions_data
        ]
        return cls(
            workflow_id=data.get("workflow_id", ""),
            user_id=data.get("user_id", ""),
            school_id=data.get("school_id", ""),
            class_id=data.get("class_id", ""),
            subject_id=data.get("subject_id", ""),
            topic=data.get("topic", ""),
            target_difficulty=data.get("target_difficulty", "medium"),
            question_count=data.get("question_count", 5),
            status=data.get("status", "pending"),
            questions=questions,
            analysis=data.get("analysis", {}),
            error=data.get("error"),
            created_at=data.get("created_at", 0),
            updated_at=data.get("updated_at", 0),
            request_id=data.get("request_id", ""),
        )

    def touch(self) -> None:
        """更新 updated_at 时间戳."""
        self.updated_at = int(datetime.now(UTC).timestamp() * 1000)


class WorkflowStateStore:
    """工作流状态存储（Redis）.

    全并行模式：Redis 不可用时降级到内存存储。
    """

    def __init__(
        self,
        redis: Redis | None = None,
        ttl_seconds: int = DEFAULT_TTL_SECONDS,
    ) -> None:
        self._redis = redis
        self._ttl = ttl_seconds
        # 内存降级存储（Redis 不可用时使用）
        self._memory_store: dict[str, str] = {}

    async def create(self, state: WorkflowState) -> WorkflowState:
        """创建工作流状态.

        Args:
            state: 工作流状态

        Returns:
            创建后的 WorkflowState（含生成的 workflow_id）
        """
        state.touch()
        key = self._key(state.workflow_id)
        value = json.dumps(state.to_dict(), ensure_ascii=False)

        if self._redis is not None:
            try:
                await self._redis.setex(key, self._ttl, value)
                logger.info(
                    "workflow_created",
                    workflow_id=state.workflow_id,
                    status=state.status,
                )
            except RedisError as exc:
                logger.warning(
                    "workflow_create_redis_failed_using_memory",
                    error=str(exc),
                )
                self._memory_store[key] = value
        else:
            self._memory_store[key] = value

        return state

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

        Args:
            workflow_id: 工作流 ID

        Returns:
            WorkflowState

        Raises:
            AIWorkflowNotFoundError: 工作流不存在或已过期
        """
        key = self._key(workflow_id)
        value: str | None = None

        if self._redis is not None:
            try:
                value = await self._redis.get(key)
            except RedisError as exc:
                logger.warning(
                    "workflow_get_redis_failed_using_memory",
                    error=str(exc),
                )
                value = self._memory_store.get(key)
        else:
            value = self._memory_store.get(key)

        if value is None:
            raise AIWorkflowNotFoundError(workflow_id)

        data = json.loads(value)
        return WorkflowState.from_dict(data)

    async def update(self, workflow_id: str, **updates: Any) -> WorkflowState:
        """更新工作流状态.

        Args:
            workflow_id: 工作流 ID
            **updates: 要更新的字段

        Returns:
            更新后的 WorkflowState

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

        for key, value in updates.items():
            if hasattr(state, key):
                setattr(state, key, value)

        state.touch()

        # 重新保存
        redis_key = self._key(workflow_id)
        serialized = json.dumps(state.to_dict(), ensure_ascii=False)

        if self._redis is not None:
            try:
                await self._redis.setex(redis_key, self._ttl, serialized)
                logger.info(
                    "workflow_updated",
                    workflow_id=workflow_id,
                    status=state.status,
                )
            except RedisError as exc:
                logger.warning(
                    "workflow_update_redis_failed_using_memory",
                    error=str(exc),
                )
                self._memory_store[redis_key] = serialized
        else:
            self._memory_store[redis_key] = serialized

        return state

    async def delete(self, workflow_id: str) -> None:
        """删除工作流.

        Args:
            workflow_id: 工作流 ID
        """
        key = self._key(workflow_id)
        if self._redis is not None:
            try:
                await self._redis.delete(key)
            except RedisError as exc:
                logger.warning("workflow_delete_redis_failed", error=str(exc))
        self._memory_store.pop(key, None)

    @staticmethod
    def _key(workflow_id: str) -> str:
        """构建 Redis key."""
        return f"{WORKFLOW_KEY_PREFIX}:{workflow_id}"
