"""
AWOOOI API Configuration
========================
Pydantic Settings + Environment Variables

ADR-005: BFF Architecture
ADR-006: AI Fallback Strategy (Ollama -> Gemini -> Claude)

Four Iron Laws:
1. Async-First
2. CORS Whitelist (NO wildcard)
3. Pydantic Config (this file)
4. structlog
"""

from functools import lru_cache
from typing import Literal

from pydantic import Field, HttpUrl, field_validator
from pydantic_settings import BaseSettings, SettingsConfigDict


class Settings(BaseSettings):
    """
    Application settings from environment variables

    All settings can be overridden via .env file or environment variables.
    """

    model_config = SettingsConfigDict(
        env_file=".env",
        env_file_encoding="utf-8",
        case_sensitive=True,
        extra="ignore",
    )

    # ==========================================================================
    # Application
    # ==========================================================================
    VERSION: str = "1.0.0"
    ENVIRONMENT: Literal["dev", "prod"] = "dev"
    DEBUG: bool = False
    LOG_LEVEL: Literal["DEBUG", "INFO", "WARNING", "ERROR"] = "INFO"
    SYSTEM_NAME: str = "awoooi"

    # ==========================================================================
    # Mock Mode - 開發時模擬外部服務
    # ==========================================================================
    MOCK_MODE: bool = Field(
        default=False,
        description="Enable mock mode for external services (Redis, Ollama, OpenClaw, PostgreSQL, SigNoz)",
    )

    # ==========================================================================
    # Phase 16: leWOOOgo 積木化絞殺者模式 (Strangler Fig Pattern)
    # 2026-03-26 統帥批准立即執行
    # 2026-04-01 ogt: Phase R-R2 完成，內嵌版本已移除，此開關已失效 (ADR-046 P2-03)
    # 回滾方式: git revert c7b3f8f d17b67c + kubectl rollout restart deployment/awoooi-api
    # ==========================================================================
    USE_NEW_ENGINE: bool = Field(
        default=True,
        description="[已失效] Phase R-R2 後內嵌版本已移除，此開關無消費者，僅保留供環境相容性",
    )

    # ==========================================================================
    # Phase 22: OpenClaw + Nemotron 協作 (ADR-044)
    # 2026-03-31 Claude Code: 統帥批准實作
    #
    # 功能:
    # - ENABLE_NEMOTRON_COLLABORATION: 啟用 OpenClaw + Nemotron 雙軌協作
    # - NEMOTRON_TIMEOUT_SECONDS: Nemotron API 呼叫超時
    # - NEMOTRON_ASYNC_UPDATE: 異步更新模式 (先推 OpenClaw，後更新 Nemotron)
    #
    # 回滾指令: kubectl set env deployment/awoooi-api ENABLE_NEMOTRON_COLLABORATION=false
    # ==========================================================================
    ENABLE_NEMOTRON_COLLABORATION: bool = Field(
        default=True,
        description="Phase 22: True=啟用 OpenClaw+Nemotron 協作, False=僅 OpenClaw",
    )
    NEMOTRON_TIMEOUT_SECONDS: int = Field(
        default=45,
        description="Phase 22: Nemotron API 呼叫超時 (秒)",
    )
    NEMOTRON_ASYNC_UPDATE: bool = Field(
        default=True,
        description="Phase 22: True=異步更新 (先推 OpenClaw), False=同步等待",
    )

    # ==========================================================================
    # CORS - 嚴格白名單 (無 UAT, 無 wildcard)
    # ==========================================================================
    CORS_ORIGINS: list[str] = Field(
        default=[
            "http://localhost:3000",
            "http://localhost:3001",
            "http://localhost:3002",
            "http://localhost:3003",
            "http://localhost:3333",
            "http://192.168.0.168:3000",  # 168 MacBook 本機開發
            "http://192.168.0.188:3000",  # 188 本機開發
            "https://awoooi.wooo.work",
        ],
        description="Allowed CORS origins - NO wildcards allowed",
    )

    @field_validator("CORS_ORIGINS", mode="before")
    @classmethod
    def parse_cors_origins(cls, v: str | list[str]) -> list[str]:
        if isinstance(v, str):
            origins = [origin.strip() for origin in v.split(",")]
        else:
            origins = v
        # Security check: reject wildcards
        if "*" in origins:
            raise ValueError("Wildcard (*) is NOT allowed in CORS_ORIGINS")
        return origins

    # ==========================================================================
    # Database (PostgreSQL on 192.168.0.188)
    # ==========================================================================
    DATABASE_URL: str = Field(
        default="postgresql+asyncpg://awoooi:changeme@192.168.0.188:5432/awoooi_prod",
        description="PostgreSQL connection URL",
    )

    # ==========================================================================
    # Redis (192.168.0.188:6380, DB 0 - 與 OpenClaw 共用)
    # ==========================================================================
    REDIS_URL: str = Field(
        default="redis://192.168.0.188:6380/0",
        description="Redis connection URL (DB 0 shared with OpenClaw)",
    )

    # ==========================================================================
    # External Services - Four Host Architecture
    # ==========================================================================
    OLLAMA_URL: str = Field(
        default="http://192.168.0.188:11434",
        description="Ollama LLM service URL",
    )
    # Deprecated: use OPENCLAW_URL instead
    CLAWBOT_URL: str = Field(
        default="http://192.168.0.188:8088",  # 🔧 修正: OpenClaw 實際 port 是 8088
        description="[Deprecated] Legacy OpenClaw URL - use OPENCLAW_URL",
    )
    KALI_SCANNER_URL: str = Field(
        default="http://192.168.0.112:8080",
        description="Kali security scanner URL",
    )
    SIGNOZ_URL: str = Field(
        default="http://192.168.0.188:3301",
        description="SigNoz observability URL",
    )
    CLICKHOUSE_URL: str = Field(
        default="http://192.168.0.188:8123",
        description="ClickHouse HTTP API URL (SignOz backend, direct query)",
    )

    # ==========================================================================
    # Sentry Self-Hosted (Phase 10: Error Tracking + AI Analysis)
    # 端點: http://192.168.0.110:9000 (DevOps 金庫)
    # ==========================================================================
    SENTRY_SELF_HOSTED_URL: str = Field(
        default="http://192.168.0.110:9000",
        description="Sentry Self-Hosted API URL",
    )
    SENTRY_ORG: str = Field(
        default="sentry",
        description="Sentry organization slug",
    )
    SENTRY_PROJECT: str = Field(
        default="awoooi-api",
        description="Sentry project slug",
    )
    SENTRY_AUTH_TOKEN: str = Field(
        default="",
        description="Sentry Auth Token for API access (from K8s Secret)",
    )

    # ==========================================================================
    # OpenTelemetry (可觀測性鐵律)
    # 四主機架構強制校驗: OTEL 必須指向 192.168.0.188
    # ==========================================================================
    OTEL_ENABLED: bool = Field(
        default=True,
        description="Enable OpenTelemetry tracing (disable in MOCK_MODE)",
    )
    OTEL_EXPORTER_OTLP_ENDPOINT: str = Field(
        default="192.168.0.188:24317",
        description="SigNoz OTLP gRPC endpoint (Host port 24317 -> Container 4317) - NO http:// prefix for gRPC",
    )
    OTEL_SERVICE_NAME: str = Field(
        default="awoooi-api",
        description="Service name for tracing",
    )
    OTEL_TRACES_SAMPLER_ARG: float = Field(
        default=1.0,
        description="Trace sampling rate (1.0 = 100%)",
    )

    # ==========================================================================
    # Langfuse LLMOps (Phase 15.1)
    # LLM 呼叫追蹤、成本監控、Prompt 版本管理
    # 端點: http://192.168.0.110:3100 (DevOps 金庫)
    # ==========================================================================
    LANGFUSE_ENABLED: bool = Field(
        default=True,
        description="Enable Langfuse LLM observability",
    )
    LANGFUSE_URL: str = Field(
        default="http://192.168.0.110:3100",
        description="Langfuse self-hosted URL",
    )
    LANGFUSE_PUBLIC_KEY: str = Field(
        default="",
        description="Langfuse public key (from K8s Secret)",
    )
    LANGFUSE_SECRET_KEY: str = Field(
        default="",
        description="Langfuse secret key (from K8s Secret)",
    )

    # ==========================================================================
    # AI Fallback Strategy (ADR-006 v1.3 + ADR-036)
    # Order: Ollama (local) -> Gemini (cloud) -> Claude (cloud)
    # Tool Calling: Nemotron (專用) -> Gemini -> Claude
    # ==========================================================================
    AI_FALLBACK_ORDER: list[str] = Field(
        default=["ollama", "gemini", "claude"],
        description="AI provider fallback order",
    )
    GEMINI_API_KEY: str = Field(default="", description="Google Gemini API key")
    CLAUDE_API_KEY: str = Field(default="", description="Anthropic Claude API key")
    # 2026-03-29 ogt: ADR-036 Nemotron Tool Calling 整合
    NVIDIA_API_KEY: str = Field(
        default="",
        description="NVIDIA NIM API key for Nemotron Tool Calling (ADR-036)",
    )

    @field_validator("AI_FALLBACK_ORDER", mode="before")
    @classmethod
    def parse_ai_fallback(cls, v: str | list[str]) -> list[str]:
        """
        解析 AI_FALLBACK_ORDER，支援三種格式:
        1. JSON: '["gemini","ollama","claude"]'
        2. CSV:  'gemini,ollama,claude'
        3. List: ["gemini", "ollama", "claude"]

        2026-03-27 修復: ConfigMap 用 JSON 格式，原本只支援 CSV
        """
        import json

        if isinstance(v, str):
            v = v.strip()
            # 嘗試 JSON 解析 (ConfigMap 格式)
            if v.startswith("["):
                try:
                    parsed = json.loads(v)
                    return [p.strip().lower() for p in parsed]
                except json.JSONDecodeError:
                    pass  # 降級到 CSV 解析
            # CSV 格式
            return [provider.strip().lower() for provider in v.split(",")]
        return [p.lower() for p in v]

    # ==========================================================================
    # Kubernetes / K3s (CTO-201)
    # ==========================================================================
    KUBECONFIG_PATH: str = Field(
        default="k3s-prod.yaml",
        description="Path to kubeconfig file for K3s cluster (192.168.0.120)",
    )
    K8S_NAMESPACE_DEFAULT: str = Field(
        default="default",
        description="Default Kubernetes namespace for operations",
    )
    K8S_OPERATION_TIMEOUT: int = Field(
        default=30,
        description="Timeout for K8s operations in seconds",
    )
    K8S_API_KEY: str = Field(
        default="",
        description="API Key for K8s admin endpoints (X-K8s-Api-Key header)",
    )

    # ==========================================================================
    # 統帥鐵律：禁止 SQLite (AWOOOI 憲法)
    # ==========================================================================
    # ❌ 已移除 SQLITE_DATABASE_URL - 違反 AWOOOI 憲法
    # 所有持久化必須使用 PostgreSQL (DATABASE_URL)
    # 審計日誌請使用 PostgreSQL audit_logs 表
    # ==========================================================================

    # ==========================================================================
    # Cache TTL (seconds)
    # ==========================================================================
    CACHE_TTL_DASHBOARD: int = Field(default=300, description="Dashboard cache TTL (5 min)")
    CACHE_TTL_HOST_STATUS: int = Field(default=30, description="Host status cache TTL (30 sec)")
    CACHE_TTL_AI_RESPONSE: int = Field(default=3600, description="AI response cache TTL (1 hour)")

    # ==========================================================================
    # Health Check Timeouts (seconds)
    # ==========================================================================
    HEALTH_CHECK_TIMEOUT: float = Field(default=5.0, description="Health check timeout")

    # ==========================================================================
    # Phase 5: OpenClaw AI Engine (正名自 OpenClaw)
    # Synced from models.json - Ollama First Strategy
    # ==========================================================================
    OPENCLAW_URL: str = Field(
        default="http://192.168.0.188:8088",  # 🔧 修正: OpenClaw 實際 port 是 8088
        description="OpenClaw AI Agent service URL",
    )
    OPENCLAW_DEFAULT_MODEL: str = Field(
        default="qwen2.5:7b-instruct",
        description="Default Ollama model for RCA analysis (7B params, better Chinese)",
    )
    OPENCLAW_TIMEOUT: int = Field(
        default=90,
        description="Timeout for OpenClaw AI calls (seconds)",
    )

    # ==========================================================================
    # Phase 5: Telegram Gateway (繼承自 AIOPS)
    # CISO 要求: Token 必須存放於 K8s Secret，此處為開發預設
    # ==========================================================================
    OPENCLAW_TG_BOT_TOKEN: str = Field(
        default="",
        description="Telegram Bot Token (from K8s Secret in prod)",
    )
    OPENCLAW_TG_CHAT_ID: str = Field(
        default="",
        description="Telegram Chat ID for notifications",
    )
    # 使用 str 避免 pydantic-settings 自動 JSON 解析
    # Pydantic v2 禁止底線開頭的 Field 名稱
    OPENCLAW_TG_USER_WHITELIST: str = Field(
        default="",
        description="Telegram user IDs allowed to sign approvals (comma-separated or JSON array)",
    )
    # 2026-03-23 架構修正 (遵循 C-Suite 決議)
    # 鐵律: .188 為唯一大腦，禁止腦分裂
    # OpenClaw (192.168.0.188) = 唯一 Telegram Gateway
    # AWOOOI API (K8s) = Web API + Sensor，不做 Polling
    TELEGRAM_ENABLE_POLLING: bool = Field(
        default=False,
        description="Telegram Polling (False: OpenClaw handles it; True: only if OpenClaw unavailable)",
    )

    def get_tg_user_whitelist(self) -> list[int]:
        """Parse comma-separated or JSON array user IDs to list[int]"""
        raw = self.OPENCLAW_TG_USER_WHITELIST
        if not raw or not raw.strip():
            return []
        # Handle JSON array format or comma-separated
        if raw.startswith("["):
            import json
            return json.loads(raw)
        return [int(uid.strip()) for uid in raw.split(",")]

    # ==========================================================================
    # Phase 5: Webhook Security (CISO 要求)
    # HMAC-SHA256 簽章驗證 + Nonce 防重放
    # ==========================================================================
    WEBHOOK_HMAC_SECRET: str = Field(
        default="",
        description="HMAC secret for webhook signature verification",
    )
    WEBHOOK_NONCE_TTL: int = Field(
        default=300,
        description="Nonce TTL in seconds for replay attack prevention",
    )

    # ==========================================================================
    # Phase 5: Shadow Mode (物理繳械)
    # 統帥戰略 C: 接入真實告警，但物理閹割 AI 破壞力
    # ==========================================================================
    SHADOW_MODE_ENABLED: bool = Field(
        default=True,
        description="Shadow Mode: Force dry-run for all K8s operations (safe by default)",
    )
    SHADOW_MODE_LOG_ONLY: bool = Field(
        default=True,
        description="Shadow Mode: Only log operations without any K8s API calls",
    )

    # ==========================================================================
    # Phase 5: Context Gatherer (首席架構師要求)
    # 日誌清洗: 僅保留 ERROR/FATAL/CRITICAL
    # ==========================================================================
    CONTEXT_LOG_LEVELS: list[str] = Field(
        default=["ERROR", "FATAL", "CRITICAL", "WARN", "WARNING"],
        description="Log levels to include in AI context (ERROR Only principle)",
    )
    CONTEXT_MAX_LINES: int = Field(
        default=100,
        description="Maximum log lines to include in context",
    )

    @field_validator("CONTEXT_LOG_LEVELS", mode="before")
    @classmethod
    def parse_log_levels(cls, v: str | list[str]) -> list[str]:
        if isinstance(v, str):
            return [level.strip().upper() for level in v.split(",")]
        return [level.upper() for level in v]

    # ==========================================================================
    # Notification Plugins (leWOOOgo Output)
    # Fail-Fast: HttpUrl 驗證確保啟動時攔截設定錯誤
    # ==========================================================================
    DISCORD_WEBHOOK_URL: str = Field(
        default="",
        description="Discord webhook URL for sending execution reports",
    )
    SLACK_WEBHOOK_URL: str = Field(
        default="",
        description="Slack webhook URL for sending execution reports",
    )
    NOTIFICATION_ENABLED: bool = Field(
        default=True,
        description="Enable post-execution notifications",
    )

    @field_validator("DISCORD_WEBHOOK_URL", "SLACK_WEBHOOK_URL", mode="before")
    @classmethod
    def validate_webhook_url(cls, v: str | None) -> str:
        """
        Fail-Fast Webhook URL 驗證

        - 空字串 = 停用 (合法)
        - 非空字串必須是合法 HttpUrl (否則啟動失敗)
        """
        if not v or v.strip() == "":
            return ""
        # Validate as HttpUrl (raises ValueError if invalid)
        HttpUrl(v)
        return v

    # ==========================================================================
    # Phase 13.1: GitHub Webhook → OpenClaw 整合
    # GitHub PR/Push 事件自動觸發 AI 代碼審查
    # ==========================================================================
    GITHUB_WEBHOOK_SECRET: str = Field(
        default="",
        description="GitHub Webhook secret for signature verification (X-Hub-Signature-256)",
    )
    GITHUB_ALLOWED_REPOS: str = Field(
        default="",
        description="Comma-separated list of allowed repositories (e.g., 'owner/repo1,owner/repo2')",
    )

    def get_github_allowed_repos(self) -> list[str]:
        """Parse comma-separated allowed repos to list"""
        raw = self.GITHUB_ALLOWED_REPOS
        if not raw or not raw.strip():
            return []
        return [repo.strip() for repo in raw.split(",") if repo.strip()]

    # ==========================================================================
    # Phase 13.2: Grafana MCP Tool (#83)
    # ==========================================================================
    GRAFANA_URL: str = Field(
        default="http://192.168.0.188:3000",
        description="Grafana server URL",
    )
    GRAFANA_API_KEY: str = Field(
        default="",
        description="Grafana API key for authentication (Bearer token)",
    )

    # ==========================================================================
    # Computed Properties
    # ==========================================================================
    @property
    def is_production(self) -> bool:
        """Check if running in production"""
        return self.ENVIRONMENT == "prod"

    @property
    def four_hosts(self) -> dict[str, str]:
        """Four host architecture reference"""
        return {
            "devops": "192.168.0.110",      # Harbor, GH Runner
            "security": "192.168.0.112",    # Kali Scanner
            "k3s_master": "192.168.0.120",  # K3s Master
            "ai_web": "192.168.0.188",      # Nginx, Postgres, Redis, Ollama
        }


@lru_cache
def get_settings() -> Settings:
    """Get cached settings instance"""
    return Settings()


# Singleton for direct import
settings = get_settings()
