# apps/api/src/services/service_registry.py
# Service Registry Client — 讀取 ops/config/service-registry.yaml
# 撰寫: Claude Sonnet 4.6 / 2026-04-08 Asia/Taipei
# 修正: 2026-04-08 Claude Sonnet 4.6 — _find_registry_path() 相容 Docker 環境
# 架構: leWOOOgo 積木化，純 Service 層，無 Router/DB 依賴
# 參考: ADR-062, ADR-063

from __future__ import annotations

import structlog
from enum import Enum
from pathlib import Path
from typing import Any

import yaml

logger = structlog.get_logger(__name__)

# YAML 路徑 — 延遲載入，import 時不 crash
# Docker 容器不包含 ops/ 目錄，所以 registry 在 K8s 環境中不可用
# 功能降級: 找不到 YAML 時 get_service_registry() 回傳空 registry
_DEFAULT_REGISTRY_PATH: Path | None = None

def _find_registry_path() -> Path | None:
    """安全搜尋 service-registry.yaml，找不到回傳 None"""
    current = Path(__file__).resolve().parent
    for _ in range(10):
        for subdir in ["ops/config", "ops/monitoring"]:
            candidate = current / subdir / "service-registry.yaml"
            if candidate.exists():
                return candidate
        if current == current.parent:
            break
        current = current.parent
    return None


class StatefulLevel(str, Enum):
    BLOCK = "BLOCK"                 # 禁止，僅告警
    CRITICAL_HITL = "CRITICAL_HITL"  # 2 票 MultiSig
    STANDARD_HITL = "STANDARD_HITL"  # 1 票
    AUTO = "AUTO"                   # 自動執行


class ServiceInfo:
    def __init__(self, data: dict[str, Any]) -> None:
        self.name: str = data["name"]
        self.display_name: str = data.get("display_name", self.name)
        self.host: str = data.get("host", "unknown")
        self.stateful_level: StatefulLevel = StatefulLevel(data.get("stateful_level", "AUTO"))
        self.reason: str = data.get("reason", "")
        self.alert_only: bool = data.get("alert_only", False)
        self.requires_pre_backup: bool = data.get("requires_pre_backup", False)
        self.restart_command: str = data.get("restart_command", "docker restart")
        self.containers: list[str] = data.get("containers", [])


class ServiceRegistryClient:
    """
    Service Registry 客戶端
    讀取 ops/config/service-registry.yaml，提供服務 Stateful 分級查詢
    設計原則: 純讀取，不寫入；失敗時 fallback AUTO（防護不應阻擋告警流程）
    """

    def __init__(self, registry_path: Path | None = None) -> None:
        self._path = registry_path or _find_registry_path()
        self._services: dict[str, ServiceInfo] = {}
        self._backup_policies: dict[str, Any] = {}
        self._multisig_config: dict[str, Any] = {}
        self._loaded = False

    def _load(self) -> None:
        if self._loaded:
            return
        if self._path is None or not self._path.exists():
            logger.warning("service_registry_not_found", path=str(self._path), fallback="all services treated as AUTO")
            self._loaded = True
            return
        try:
            with open(self._path) as f:
                data = yaml.safe_load(f)
            for svc in data.get("services", []):
                info = ServiceInfo(svc)
                self._services[info.name] = info
                # 也按 container 名稱建立索引
                for container in info.containers:
                    self._services[container] = info
            self._backup_policies = data.get("backup_policies", {})
            self._multisig_config = data.get("multisig", {})
            self._loaded = True
            logger.info(f"Service Registry 載入完成: {len(self._services)} 個服務")
        except Exception as e:
            logger.error(f"Service Registry 載入失敗: {e}，所有服務 fallback AUTO")
            self._loaded = True  # 防止重複嘗試

    def get_service(self, name: str) -> ServiceInfo | None:
        self._load()
        return self._services.get(name)

    def get_stateful_level(self, service_name: str) -> StatefulLevel:
        """查詢服務分級，未知服務 fallback AUTO"""
        info = self.get_service(service_name)
        if info is None:
            logger.warning(f"未知服務 '{service_name}'，fallback AUTO")
            return StatefulLevel.AUTO
        return info.stateful_level

    def is_blocked(self, service_name: str) -> bool:
        return self.get_stateful_level(service_name) == StatefulLevel.BLOCK

    def requires_multisig(self, service_name: str) -> bool:
        return self.get_stateful_level(service_name) == StatefulLevel.CRITICAL_HITL

    def get_required_votes(self, service_name: str) -> int:
        self._load()
        level = self.get_stateful_level(service_name)
        if level == StatefulLevel.CRITICAL_HITL:
            return self._multisig_config.get("critical_required_votes", 2)
        return self._multisig_config.get("standard_required_votes", 1)

    def get_backup_policies(self) -> dict[str, Any]:
        self._load()
        return self._backup_policies

    def get_restart_command(self, service_name: str) -> str:
        info = self.get_service(service_name)
        return info.restart_command if info else "docker restart"


# Singleton
_registry_client: ServiceRegistryClient | None = None


def get_service_registry() -> ServiceRegistryClient:
    global _registry_client
    if _registry_client is None:
        _registry_client = ServiceRegistryClient()
    return _registry_client


def set_service_registry(client: ServiceRegistryClient) -> None:
    """測試注入用 (P4 規範)"""
    global _registry_client
    _registry_client = client
