"""
MCP Credential Resolver — k8s Secret 參照解析
=============================================
AwoooP Phase 5.5: ADR-118 Credential Isolation
2026-05-04 ogt + Claude Sonnet 4.6

設計原則（2026-04-18 Secret Leak 事故教訓）：
  - 明文 credential 絕不進入 audit log / LLM context
  - Gateway 只傳 k8s secret ref（格式："namespace/secret-name#key"）
  - 真實 secret value 在記憶體中短暫存在，使用後立刻清除
  - 回傳給 caller 時只提供「遮罩版」（前 4 字元 + *** + 後 4 字元）
  - sha256(actual_value) 記入 awooop_mcp_credential_refs.value_sha256（指紋，不可還原）

k8s secret ref 格式：
  "namespace/secret-name#key"
  例："awoooi/telegram-bot#TELEGRAM_BOT_TOKEN"

解析方式（兩種，依環境）：
  1. k8s in-cluster：使用 kubernetes asyncclient（prod）
  2. 本機開發 fallback：讀 AWOOOP_DEV_SECRETS_JSON 環境變數（dev only）
"""

from __future__ import annotations

import hashlib
import os
import re

import structlog

logger = structlog.get_logger(__name__)

# k8s secret ref 格式正則（與 DB CHECK 一致）
_K8S_REF_RE = re.compile(r"^([a-z0-9-]+)/([a-z0-9-]+)#([a-zA-Z0-9_-]+)$")

# dev fallback：JSON 格式 {"namespace/secret-name#key": "actual_value"}
_DEV_SECRETS_ENV = "AWOOOP_DEV_SECRETS_JSON"


class CredentialResolutionError(Exception):
    error_code = "E-MCP-GATE-009"


def _mask_secret(value: str) -> str:
    """回傳遮罩版：前 4 + *** + 後 4（若長度 < 8 則全遮罩）"""
    if len(value) < 8:
        return "***"
    return f"{value[:4]}***{value[-4:]}"


def _sha256_secret(value: str) -> str:
    return hashlib.sha256(value.encode()).hexdigest()


async def resolve_k8s_secret(ref: str) -> tuple[str, str, str]:
    """
    解析 k8s secret ref，回傳 (actual_value, masked_value, sha256)。

    actual_value：明文，caller 必須在使用後清除（不可存入任何持久化層）
    masked_value：供 log / response 使用
    sha256：供 awooop_mcp_credential_refs.value_sha256 記錄

    Raises:
        CredentialResolutionError: ref 格式錯誤或 secret 不存在
    """
    m = _K8S_REF_RE.match(ref)
    if not m:
        raise CredentialResolutionError(
            f"k8s secret ref 格式錯誤（期望 'namespace/secret-name#key'）：{ref!r}"
        )

    namespace, secret_name, key = m.group(1), m.group(2), m.group(3)

    # Dev fallback：讀環境變數
    dev_json = os.environ.get(_DEV_SECRETS_ENV)
    if dev_json:
        try:
            import json
            dev_secrets: dict[str, str] = json.loads(dev_json)
            value = dev_secrets.get(ref)
            if value is None:
                raise CredentialResolutionError(
                    f"dev secrets 中找不到 ref={ref!r}"
                )
            logger.debug("credential_resolved_dev", ref=ref)
            return value, _mask_secret(value), _sha256_secret(value)
        except CredentialResolutionError:
            raise
        except Exception as exc:
            raise CredentialResolutionError(
                f"AWOOOP_DEV_SECRETS_JSON 解析失敗: {exc}"
            ) from exc

    # Production：k8s in-cluster
    try:
        from kubernetes_asyncio import client, config  # type: ignore[import]
        from kubernetes_asyncio.client import CoreV1Api  # type: ignore[import]

        await config.load_incluster_config()
        async with client.ApiClient() as api:
            v1 = CoreV1Api(api)
            secret = await v1.read_namespaced_secret(secret_name, namespace)

        if secret.data is None or key not in secret.data:
            raise CredentialResolutionError(
                f"k8s secret '{namespace}/{secret_name}' 中找不到 key='{key}'"
            )

        import base64
        encoded = secret.data[key]
        value = base64.b64decode(encoded).decode()

        logger.info(
            "credential_resolved_k8s",
            namespace=namespace,
            secret_name=secret_name,
            key=key,
            masked=_mask_secret(value),
        )
        return value, _mask_secret(value), _sha256_secret(value)

    except CredentialResolutionError:
        raise
    except ImportError:
        raise CredentialResolutionError(
            "kubernetes_asyncio 未安裝，且未設定 AWOOOP_DEV_SECRETS_JSON（dev fallback）"
        )
    except Exception as exc:
        logger.exception(
            "credential_resolution_k8s_failed",
            ref=ref,
            error=str(exc),
        )
        raise CredentialResolutionError(
            f"k8s secret 解析失敗（{namespace}/{secret_name}#{key}）: {exc}"
        ) from exc
