"""L5 FlowOrchestrator：编排幂等/熔断/会话缓存/队列/Flow。

send_text 流程：
1. 幂等检查（命中返回 skipped=true）
2. DB 校验熔断检查（OPEN 时返回 BRIDGE_CIRCUITED）
3. 入队（自适应 delay_ms：同联系人 1000 / 不同 config.send_delay_ms）
4. 成功且 verified 才写幂等缓存
5. DB 校验失败计数 + 熔断
6. 失败时 session_cache.invalidate()
"""

from __future__ import annotations

import asyncio
import logging
import time
from typing import Optional

from woc_bridge.ui.circuit_breaker import CircuitBreaker, CircuitState
from woc_bridge.ui.errors import FlowError, is_retryable
from woc_bridge.ui.flow import FlowContext, FlowResult, FlowState
from woc_bridge.ui.idem_cache import IdemCache

logger = logging.getLogger("woc-bridge")

# 同联系人发送间隔（毫秒），比默认 send_delay_ms 短
_SAME_CONTACT_DELAY_MS = 1000


class SessionCache:
    """会话缓存：记录最近打开的会话名，30s TTL。

    命中时 SendTextFlow 跳过 activate/click_search_box/type_query/open_session 四步，
    直接校验会话仍打开后进入 focus_input。
    """

    def __init__(self, ttl: float = 30.0) -> None:
        self.ttl = ttl
        self._current: Optional[tuple[str, float]] = None  # (to_wxid, expire_at)

    def get(self) -> Optional[str]:
        """返回当前缓存的 to_wxid，过期或无缓存返回 None。"""
        if self._current is None:
            return None
        to_wxid, expire_at = self._current
        if time.monotonic() >= expire_at:
            self._current = None
            return None
        return to_wxid

    def set(self, to_wxid: str) -> None:
        """更新缓存。"""
        self._current = (to_wxid, time.monotonic() + self.ttl)

    def invalidate(self) -> None:
        """失效缓存。"""
        self._current = None


class FlowOrchestrator:
    """L5 Flow 编排器。

    整合幂等缓存、熔断器、会话缓存、发送队列、Flow 执行。
    """

    def __init__(
        self,
        send_queue,
        idem_cache: IdemCache,
        actions,
        db_reader,
        config,
        send_text_flow: Optional[SendTextFlow] = None,
    ) -> None:
        """初始化编排器。

        Args:
            send_queue: SendQueue 实例
            idem_cache: IdemCache 实例
            actions: L3 Actions 实例
            db_reader: DbReader 实例
            config: BridgeConfig 实例（含 send_delay_ms 等）
            send_text_flow: 可选的 SendTextFlow 实例（用于测试注入）
        """
        self.send_queue = send_queue
        self.idem_cache = idem_cache
        self.actions = actions
        self.db_reader = db_reader
        self.config = config
        self.session_cache = SessionCache(ttl=30.0)
        # DB 校验熔断器：5 次失败 / 30s 恢复
        self.db_verify_breaker = CircuitBreaker(
            "db_verify", failure_threshold=5, recovery_timeout=30.0
        )
        self._send_text_flow = send_text_flow

    def _get_send_text_flow(self):
        """获取 SendTextFlow 实例（惰性创建或使用注入的）。"""
        if self._send_text_flow is not None:
            return self._send_text_flow
        # 惰性导入避免循环依赖
        from woc_bridge.ui.flows.send_text import SendTextFlow
        return SendTextFlow(
            actions=self.actions,
            db_reader=self.db_reader,
            session_cache=self.session_cache,
        )

    async def send_text(
        self,
        to_wxid: str,
        content: str,
        display_name: str = "",
        client_request_id: str = "",
    ) -> FlowResult:
        """发送文本消息。

        流程：
        1. 幂等检查
        2. DB 校验熔断检查
        3. 自适应延时入队
        4. 执行 SendTextFlow
        5. 成功且 verified 写幂等缓存
        6. 失败时失效会话缓存

        Args:
            to_wxid: 目标 wxid
            content: 消息内容
            display_name: 显示名（搜索用）
            client_request_id: 客户端幂等 ID

        Returns:
            FlowResult
        """
        started_at = time.monotonic()

        # 1. 幂等检查
        cached = self.idem_cache.get(
            "send_text", to_wxid, content, client_request_id
        )
        if cached is not None:
            logger.info(
                "[orchestrator] idem cache hit, skip send (to_wxid=%s)",
                to_wxid,
            )
            # 返回缓存的 FlowResult，标记 skipped
            if isinstance(cached, FlowResult):
                return FlowResult(
                    success=True,
                    local_id=cached.local_id,
                    verified=cached.verified,
                    skipped=True,
                    state=cached.state,
                    duration_ms=(time.monotonic() - started_at) * 1000,
                )
            # 兜底：构造一个 skipped 结果
            return FlowResult(
                success=True,
                skipped=True,
                duration_ms=(time.monotonic() - started_at) * 1000,
            )

        # 2. DB 校验熔断检查
        if not self.db_verify_breaker.allow():
            logger.warning(
                "[orchestrator] db_verify breaker OPEN, reject send"
            )
            return FlowResult(
                success=False,
                error="db_verify circuit breaker open",
                error_code="BRIDGE_CIRCUITED",
                state=FlowState.FAILED,
                duration_ms=(time.monotonic() - started_at) * 1000,
            )

        # 3. 自适应延时：同联系人用短延时
        cached_session = self.session_cache.get()
        is_same_contact = (
            cached_session is not None and cached_session == to_wxid
        )
        delay_ms = _SAME_CONTACT_DELAY_MS if is_same_contact else None
        # None 表示用 send_queue 的默认 send_delay_ms

        # 4. 构造 FlowContext
        ctx = FlowContext(
            to_wxid=to_wxid,
            content=content,
            display_name=display_name,
            client_request_id=client_request_id,
        )

        # 5. 入队执行
        flow = self._get_send_text_flow()
        try:
            result: FlowResult = await self.send_queue.enqueue(
                lambda: flow.run(ctx),
                delay_ms=delay_ms,
            )
        except Exception as exc:
            logger.error(
                "[orchestrator] send_queue exception: %s", exc
            )
            # 失败时失效会话缓存
            self.session_cache.invalidate()
            # 保留 BridgeError 的原始错误码（如 RATE_LIMITED），
            # 避免限流/超时等可重试错误被吞成 SEND_FAILED
            error_code = "SEND_FAILED"
            if hasattr(exc, "code") and exc.code:
                error_code = exc.code
            # 传递 details（如 retry_after）供路由层设置 Retry-After 头
            exc_details = None
            if hasattr(exc, "details") and exc.details:
                exc_details = exc.details
            return FlowResult(
                success=False,
                error=str(exc),
                error_code=error_code,
                state=FlowState.FAILED,
                duration_ms=(time.monotonic() - started_at) * 1000,
                details=exc_details,
            )

        # 6. 结果处理
        if result.success and result.verified:
            # 成功且 verified：写幂等缓存
            self.idem_cache.set(
                "send_text", to_wxid, content, result, client_request_id
            )
            self.db_verify_breaker.record_success()
        elif result.success and not result.verified:
            # 发送成功但校验失败：DB 校验熔断计数
            self.db_verify_breaker.record_failure()
            logger.warning(
                "[orchestrator] send success but verify failed, breaker fail_count+1"
            )
        else:
            # 发送失败：失效会话缓存
            self.session_cache.invalidate()
            logger.warning(
                "[orchestrator] send failed: %s (code=%s)",
                result.error, result.error_code,
            )

        return result
