"""
K8s Resource Naming Utilities - ADR-016 資源名稱規範
=====================================================

提供 K8s 資源名稱正規化與驗證功能：
1. URL/域名 → 有效 K8s 名稱
2. 格式驗證 (RFC 1123)
3. 靜態映射表查詢

K8s 命名規則 (RFC 1123):
- 最多 63 字元
- 只能包含小寫字母、數字、連字號
- 必須以字母或數字開頭和結尾

版本: v1.0
建立: 2026-03-26 (台北時區)
建立者: Claude Code (首席架構師)

@see docs/adr/ADR-016-k8s-resource-naming.md
"""

import re
from dataclasses import dataclass
from enum import Enum
from typing import Final

import structlog

logger = structlog.get_logger(__name__)


# =============================================================================
# Constants
# =============================================================================

# K8s 名稱正則 (RFC 1123 subdomain)
K8S_NAME_PATTERN: Final[re.Pattern] = re.compile(
    r"^[a-z0-9]([-a-z0-9]*[a-z0-9])?$"
)

# 最大長度
K8S_NAME_MAX_LENGTH: Final[int] = 63


# =============================================================================
# Static Mapping Table (Fallback)
# =============================================================================

# URL/域名 → K8s Deployment 映射
# 當動態查詢失敗時使用
RESOURCE_MAPPING: Final[dict[str, tuple[str, str]]] = {
    # 域名 → (deployment_name, namespace)
    "api.awoooi.wooo.work": ("awoooi-api", "awoooi-prod"),
    "awoooi.wooo.work": ("awoooi-web", "awoooi-prod"),
    "wooo.work": ("awoooi-web", "awoooi-prod"),

    # 服務別名
    "awoooi-api": ("awoooi-api", "awoooi-prod"),
    "awoooi-web": ("awoooi-web", "awoooi-prod"),
    "openclaw": ("openclaw", "awoooi-prod"),

    # 內部服務
    "signoz": ("signoz-otel-collector", "signoz"),
    "langfuse": ("langfuse-web", "langfuse"),
}

# 非 K8s 資源標記 (這些主機不在 K8s 中)
NON_K8S_HOSTS: Final[set[str]] = {
    "prod-docker-188",
    "192.168.0.188",
    "192.168.0.110",
    "192.168.0.112",
}


# =============================================================================
# Types
# =============================================================================

class ResourceType(str, Enum):
    """資源類型"""
    DEPLOYMENT = "deployment"
    STATEFULSET = "statefulset"
    POD = "pod"
    SERVICE = "service"
    UNKNOWN = "unknown"


@dataclass
class NormalizeResult:
    """正規化結果"""
    success: bool
    original: str
    normalized: str | None
    namespace: str | None
    resource_type: ResourceType
    is_k8s_resource: bool
    confidence: float  # 0.0 - 1.0
    note: str | None = None


# =============================================================================
# Normalization Functions
# =============================================================================

def is_valid_k8s_name(name: str) -> bool:
    """
    檢查是否為有效的 K8s 資源名稱 (RFC 1123)

    Args:
        name: 資源名稱

    Returns:
        bool: 是否有效
    """
    if not name:
        return False
    if len(name) > K8S_NAME_MAX_LENGTH:
        return False
    return bool(K8S_NAME_PATTERN.match(name))


def strip_url_scheme(raw: str) -> str:
    """
    移除 URL scheme 和路徑

    Examples:
        https://api.awoooi.wooo.work/v1/health → api.awoooi.wooo.work
        http://192.168.0.188:8000 → 192.168.0.188
    """
    # 移除 scheme
    result = re.sub(r"^https?://", "", raw)
    # 移除 port
    result = re.sub(r":\d+.*$", "", result)
    # 移除路徑
    result = result.split("/")[0]
    return result.strip()


def to_k8s_safe_name(raw: str) -> str:
    """
    轉換為 K8s 安全名稱

    Examples:
        api.awoooi.wooo.work → api-awoooi-wooo-work
        My_Service_Name → my-service-name
    """
    # 轉小寫
    result = raw.lower()
    # 替換不允許的字元為連字號
    result = re.sub(r"[^a-z0-9-]", "-", result)
    # 合併多個連字號
    result = re.sub(r"-+", "-", result)
    # 移除開頭和結尾的連字號
    result = result.strip("-")
    # 截斷到最大長度
    if len(result) > K8S_NAME_MAX_LENGTH:
        result = result[:K8S_NAME_MAX_LENGTH].rstrip("-")
    return result


def normalize_resource_name(raw: str, default_namespace: str = "awoooi-prod") -> NormalizeResult:
    """
    正規化資源名稱 - 主入口函數

    流程：
    1. 檢查是否為非 K8s 資源
    2. 移除 URL scheme
    3. 查詢靜態映射表
    4. 轉換為 K8s 安全名稱
    5. 驗證格式

    Args:
        raw: 原始資源名稱 (可能是 URL、域名、或 K8s 名稱)
        default_namespace: 預設命名空間

    Returns:
        NormalizeResult: 正規化結果
    """
    if not raw:
        return NormalizeResult(
            success=False,
            original=raw,
            normalized=None,
            namespace=None,
            resource_type=ResourceType.UNKNOWN,
            is_k8s_resource=False,
            confidence=0.0,
            note="Empty resource name",
        )

    # Step 1: 檢查非 K8s 資源
    stripped = strip_url_scheme(raw)
    if stripped in NON_K8S_HOSTS or raw in NON_K8S_HOSTS:
        logger.info(
            "resource_is_non_k8s",
            original=raw,
            stripped=stripped,
        )
        return NormalizeResult(
            success=True,
            original=raw,
            normalized=stripped,
            namespace=None,
            resource_type=ResourceType.UNKNOWN,
            is_k8s_resource=False,
            confidence=1.0,
            note="Non-K8s host (VM/Container)",
        )

    # Step 2: 查詢靜態映射表
    lookup_key = stripped.lower()
    if lookup_key in RESOURCE_MAPPING:
        deployment, namespace = RESOURCE_MAPPING[lookup_key]
        logger.info(
            "resource_mapped_from_table",
            original=raw,
            deployment=deployment,
            namespace=namespace,
        )
        return NormalizeResult(
            success=True,
            original=raw,
            normalized=deployment,
            namespace=namespace,
            resource_type=ResourceType.DEPLOYMENT,
            is_k8s_resource=True,
            confidence=1.0,
            note="Mapped from static table",
        )

    # Step 3: 檢查是否已經是有效的 K8s 名稱
    if is_valid_k8s_name(raw):
        logger.info(
            "resource_already_valid",
            original=raw,
        )
        return NormalizeResult(
            success=True,
            original=raw,
            normalized=raw,
            namespace=default_namespace,
            resource_type=ResourceType.DEPLOYMENT,
            is_k8s_resource=True,
            confidence=0.0,  # 🔴 規則驗證，非 AI
            note="Already valid K8s name",
        )

    # Step 4: 嘗試轉換
    converted = to_k8s_safe_name(stripped)

    if is_valid_k8s_name(converted):
        logger.info(
            "resource_converted",
            original=raw,
            converted=converted,
        )
        return NormalizeResult(
            success=True,
            original=raw,
            normalized=converted,
            namespace=default_namespace,
            resource_type=ResourceType.DEPLOYMENT,
            is_k8s_resource=True,
            confidence=0.0,  # 🔴 規則轉換，非 AI
            note=f"Converted from '{raw}' (requires validation)",
        )

    # Step 5: 無法處理
    logger.warning(
        "resource_normalization_failed",
        original=raw,
        attempted=converted,
    )
    return NormalizeResult(
        success=False,
        original=raw,
        normalized=None,
        namespace=None,
        resource_type=ResourceType.UNKNOWN,
        is_k8s_resource=False,
        confidence=0.0,
        note=f"Cannot normalize '{raw}' to valid K8s name",
    )


def extract_resource_hints(raw: str) -> list[str]:
    """
    從原始名稱提取可能的資源關鍵字

    用於模糊匹配時的候選生成

    Examples:
        https://api.awoooi.wooo.work → ["api", "awoooi", "wooo", "work"]
        prod-docker-188 → ["prod", "docker", "188"]
    """
    stripped = strip_url_scheme(raw)
    # 分割所有非字母數字字元
    parts = re.split(r"[^a-z0-9]+", stripped.lower())
    # 過濾空字串和太短的詞
    return [p for p in parts if len(p) >= 2]
