"""bridge 配置与全局状态。

集中管理：
- DIAGNOSTIC_ITEMS：诊断项定义
- InitState：DB 初始化后台任务状态
- BridgeConfig：命令行参数 + 环境变量配置
- AppState：运行期共享状态容器（单例 _state）
- _require_* 辅助函数：依赖检查（未初始化时抛 BridgeError）
"""

from __future__ import annotations

import argparse
import asyncio
import logging
import os
import threading
from dataclasses import dataclass, field
from typing import Any, Callable, Optional

from woc_bridge.db import DbReader, KeyCache
from woc_bridge.messaging import MessageStreamer, SendQueue
from woc_bridge.models import BridgeError
from woc_bridge.ui import QrCapture, XdotoolDriver


# ---------------------------------------------------------------------------
# 诊断项定义（spec 11.1 节）
# ---------------------------------------------------------------------------
DIAGNOSTIC_ITEMS = [
    {
        "check_id": "wechat_running",
        "name": "微信进程检查",
        "severity": "critical",
        "description": "检查微信客户端是否运行",
        "auto_repairable": True,
    },
    {
        "check_id": "login_state",
        "name": "登录状态检查",
        "severity": "critical",
        "description": "检查微信是否已登录",
        "auto_repairable": False,
    },
    {
        "check_id": "db_accessible",
        "name": "数据库可达性",
        "severity": "error",
        "description": "检查微信 DB 是否可读",
        "auto_repairable": True,  # 可通过自动提取 key 修复
    },
    {
        "check_id": "xdotool_available",
        "name": "xdotool 可用性",
        "severity": "error",
        "description": "检查 xdotool 是否可用",
        "auto_repairable": True,
    },
]


# 诊断项 check_id → 定义映射，便于查找
_DIAGNOSTIC_ITEM_BY_ID = {item["check_id"]: item for item in DIAGNOSTIC_ITEMS}


# ---------------------------------------------------------------------------
# DB 初始化后台任务状态
# ---------------------------------------------------------------------------
class InitState:
    """DB 初始化（密钥提取）后台任务状态。"""

    def __init__(self) -> None:
        self.state: str = "idle"  # idle / running / success / failed
        self.progress_pct: Optional[float] = None
        self.message: Optional[str] = None
        self.key_count: Optional[int] = None
        self.error: Optional[str] = None
        self._thread: Optional[threading.Thread] = None

    def start(self, target: Callable, args: tuple = ()) -> None:
        """启动后台初始化线程。

        若前一个线程仍存活，拒绝重复启动（调用方应先检查 state == running）。
        """
        if self._thread is not None and self._thread.is_alive():
            return
        self.state = "running"
        self.progress_pct = 0.0
        self.message = "开始初始化"
        self.error = None
        self._thread = threading.Thread(target=target, args=args, daemon=True)
        self._thread.start()

    def set_progress(self, pct: float, message: str) -> None:
        self.progress_pct = pct
        self.message = message

    def set_success(self, key_count: int, message: str) -> None:
        self.state = "success"
        self.progress_pct = 100.0
        self.key_count = key_count
        self.message = message
        self.error = None

    def set_failed(self, error: str) -> None:
        self.state = "failed"
        self.progress_pct = None
        self.error = error
        self.message = f"初始化失败: {error}"


# ---------------------------------------------------------------------------
# BridgeConfig
# ---------------------------------------------------------------------------
def _parse_list(s: str) -> list[str]:
    """逗号分隔字符串转列表（供 BridgeConfig 解析环境变量用）。"""
    if not s.strip():
        return []
    return [item.strip() for item in s.split(",") if item.strip()]


@dataclass
class BridgeConfig:
    """bridge 服务的配置项（来自命令行参数 + 环境变量）。

    在启动时一次性构造并注入 AppState，运行期只读不写。
    所有 os.environ.get / 类型转换集中在此处，避免散落。
    """

    # 命令行参数
    listen: str = "0.0.0.0:8088"
    wechat_db: str = "/config"
    display: str = ":1"
    # 环境变量
    send_delay_ms: int = 3000
    max_calls_per_sec: int = 10
    max_queue_size: int = 100
    send_queue_wait_timeout_ms: int = 15000
    max_batch_size: int = 50
    poll_interval_ms: int = 2000
    db_key: str = ""
    auto_extract_enabled: bool = True
    # UI 自动化后端选择：flow（新六层架构，默认）/ legacy（旧 xdotool_driver 单文件）
    ui_backend: str = "flow"
    # 调试截图写盘开关：生产环境必须 false，调试模式 true 时异步队列后台写盘
    ui_debug_shots: bool = False
    # 好友自动通过配置
    auto_accept_enabled: bool = False
    auto_accept_all: bool = False
    auto_accept_whitelist_wxids: list[str] = field(default_factory=list)
    auto_accept_whitelist_nicknames: list[str] = field(default_factory=list)
    auto_accept_keywords: list[str] = field(default_factory=list)
    auto_accept_blacklist_wxids: list[str] = field(default_factory=list)
    auto_accept_blacklist_nicknames: list[str] = field(default_factory=list)
    auto_accept_allow_scenes: list[str] = field(default_factory=list)
    auto_accept_poll_interval: float = 3.0
    # 聊天记录导出（Task 17-21）
    # 导出文件根目录，每个任务建 <task_id>/ 子目录
    export_dir: str = "/config/woc-export"
    # 导出任务 TTL（秒），过期后下载链接失效并被懒清理
    export_task_ttl_sec: int = 7200
    # 群发消息风控（Task 13-16）
    batch_max_per_batch: int = 200          # 单批目标数上限（去重后）
    batch_daily_limit: int = 3              # 每日群发批次数上限
    batch_min_interval_sec: int = 7200      # 相邻两批群发最小间隔（秒，默认 2 小时）

    @classmethod
    def from_args_and_env(cls, args: argparse.Namespace) -> "BridgeConfig":
        """从 argparse 结果 + 环境变量一次性构造配置。"""
        return cls(
            listen=args.listen,
            wechat_db=args.wechat_db,
            display=args.display,
            send_delay_ms=int(os.environ.get("WOC_BRIDGE_SEND_DELAY_MS", "3000")),
            max_calls_per_sec=int(os.environ.get("WOC_BRIDGE_MAX_CALLS_PER_SEC", "10")),
            max_queue_size=int(os.environ.get("WOC_BRIDGE_MAX_QUEUE_SIZE", "100")),
            send_queue_wait_timeout_ms=int(os.environ.get("WOC_BRIDGE_SEND_QUEUE_WAIT_TIMEOUT_MS", "15000")),
            max_batch_size=int(os.environ.get("WOC_BRIDGE_MAX_BATCH_SIZE", "50")),
            poll_interval_ms=int(os.environ.get("WOC_BRIDGE_POLL_INTERVAL_MS", "2000")),
            db_key=os.environ.get("WOC_DB_KEY", "").strip(),
            auto_extract_enabled=os.environ.get(
                "WOC_DB_KEY_AUTO_EXTRACT", "true"
            ).lower() not in ("false", "0", "no", "off"),
            ui_backend=os.environ.get("WOC_UI_BACKEND", "flow").lower(),
            ui_debug_shots=os.environ.get("WOC_UI_DEBUG_SHOTS", "false").lower() in ("true", "1", "yes", "on"),
            auto_accept_enabled=os.environ.get(
                "WOC_AUTO_ACCEPT_ENABLED", "false"
            ).lower() in ("true", "1", "yes", "on"),
            auto_accept_all=os.environ.get(
                "WOC_AUTO_ACCEPT_ALL", "false"
            ).lower() in ("true", "1", "yes", "on"),
            auto_accept_whitelist_wxids=_parse_list(
                os.environ.get("WOC_AUTO_ACCEPT_WHITELIST_WXIDS", "")
            ),
            auto_accept_whitelist_nicknames=_parse_list(
                os.environ.get("WOC_AUTO_ACCEPT_WHITELIST_NICKNAMES", "")
            ),
            auto_accept_keywords=_parse_list(
                os.environ.get("WOC_AUTO_ACCEPT_KEYWORDS", "")
            ),
            auto_accept_blacklist_wxids=_parse_list(
                os.environ.get("WOC_AUTO_ACCEPT_BLACKLIST_WXIDS", "")
            ),
            auto_accept_blacklist_nicknames=_parse_list(
                os.environ.get("WOC_AUTO_ACCEPT_BLACKLIST_NICKNAMES", "")
            ),
            auto_accept_allow_scenes=_parse_list(
                os.environ.get("WOC_AUTO_ACCEPT_ALLOW_SCENES", "")
            ),
            auto_accept_poll_interval=float(
                os.environ.get("WOC_AUTO_ACCEPT_POLL_INTERVAL", "3")
            ),
            # 聊天记录导出（Task 17-21）
            export_dir=os.environ.get("WOC_EXPORT_DIR", "/config/woc-export").strip(),
            export_task_ttl_sec=int(
                os.environ.get("WOC_EXPORT_TASK_TTL_SEC", "7200")
            ),
            # 群发消息风控（Task 13-16）
            batch_max_per_batch=int(
                os.environ.get("WOC_BATCH_MAX_PER_BATCH", "200")
            ),
            batch_daily_limit=int(
                os.environ.get("WOC_BATCH_DAILY_LIMIT", "3")
            ),
            batch_min_interval_sec=int(
                os.environ.get("WOC_BATCH_MIN_INTERVAL_SEC", "7200")
            ),
        )


# ---------------------------------------------------------------------------
# AppState
# ---------------------------------------------------------------------------
class AppState:
    """运行期共享状态容器。"""

    def __init__(self) -> None:
        self.config: BridgeConfig = BridgeConfig()
        self.xdotool: XdotoolDriver | None = None
        self.db_reader: DbReader | None = None
        self.send_queue: SendQueue | None = None
        self.qr_capture: QrCapture | None = None
        self.message_streamer: MessageStreamer | None = None
        # SSE 发送结果校验总线（send_text 校验，避免 DB 轮询雪崩）
        self.verify_bus: Optional[Any] = None  # SSEVerifyBus
        self.start_time: float = 0.0
        # DB 解密密钥缓存（env / api / auto_extract / file 注入）
        self.key_cache = KeyCache()
        # DB 初始化后台任务状态
        self.init_state = InitState()
        # 自动提取异步锁：防止并发请求同时触发内存扫描
        # asyncio.Lock 需在事件循环中创建，延迟到 lifespan 首次使用时初始化
        self._extract_lock: asyncio.Lock | None = None
        # P3 新增：UI 自动化新架构组件（仅 ui_backend != "legacy" 时构造）
        # 使用 Optional[Any] 避免循环 import（不直接 import UI 模块类型）
        self.capabilities: Optional[Any] = None  # WeChatCapabilities
        self.orchestrator: Optional[Any] = None  # FlowOrchestrator
        self.watchdog: Optional[Any] = None  # WeChatWatchdog
        self.resource_reaper: Optional[Any] = None  # ResourceReaper
        self.actions: Optional[Any] = None  # Actions
        self.locator: Optional[Any] = None  # LocatorRegistry
        self.opencv_backend: Optional[Any] = None  # OpenCVBackend
        self.xdotool_backend: Optional[Any] = None  # XdotoolBackend
        self.idem_cache: Optional[Any] = None  # IdemCache
        # 好友自动通过组件
        self.friend_watcher: Optional[Any] = None      # FriendRequestWatcher
        self.rule_engine: Optional[Any] = None         # AcceptRuleEngine
        self.accept_breaker: Optional[Any] = None      # CircuitBreaker
        # 登录状态守卫（后台检测微信登录状态，供后台任务做登录前提保护）
        self.login_guard: Optional[Any] = None         # LoginGuard
        # 群发消息后台协程（Task 13-16）
        self.batch_worker: Optional[Any] = None        # BatchSendWorker

    def extract_lock(self) -> asyncio.Lock:
        """懒初始化 asyncio.Lock（需在事件循环中创建）。"""
        if self._extract_lock is None:
            self._extract_lock = asyncio.Lock()
        return self._extract_lock


_state = AppState()


# ---------------------------------------------------------------------------
# 依赖检查辅助函数（替代 assert，兼容 python -O）
# ---------------------------------------------------------------------------
def _require_xdotool() -> XdotoolDriver:
    """返回 xdotool 驱动，未初始化时抛 BridgeError。"""
    if _state.xdotool is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="xdotool driver 未初始化",
        )
    return _state.xdotool


def _require_db_reader() -> DbReader:
    """返回 DB 读取器，未初始化时抛 BridgeError。"""
    if _state.db_reader is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="db_reader 未初始化",
        )
    return _state.db_reader


def _require_send_queue() -> SendQueue:
    """返回发送队列，未初始化时抛 BridgeError。"""
    if _state.send_queue is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="send_queue 未初始化",
        )
    return _state.send_queue


def _require_qr_capture() -> QrCapture:
    """返回二维码截图器，未初始化时抛 BridgeError。"""
    if _state.qr_capture is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="qr_capture 未初始化",
        )
    return _state.qr_capture


def _require_message_streamer() -> MessageStreamer:
    """返回消息流推送器，未初始化时抛 BridgeError。"""
    if _state.message_streamer is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="message_streamer 未初始化",
        )
    return _state.message_streamer


def _require_rule_engine():
    """返回好友自动通过规则引擎，未初始化时抛 BridgeError。"""
    if _state.rule_engine is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="rule_engine 未初始化",
        )
    return _state.rule_engine


def _require_friend_watcher():
    """返回好友申请监听器，未初始化时抛 BridgeError。"""
    if _state.friend_watcher is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="friend_watcher 未初始化",
        )
    return _state.friend_watcher


def _require_batch_worker():
    """返回群发消息后台协程，未初始化时抛 BridgeError。"""
    if _state.batch_worker is None:
        raise BridgeError(
            code="BRIDGE_INTERNAL_ERROR",
            message="batch_worker 未初始化（群发功能不可用）",
        )
    return _state.batch_worker
