"""提供商 API Key 的对称加解密与掩码（ARCH §4.7）。

- 加密用 `cryptography` 的 Fernet，密钥取 `settings.credential_enc_key`。
- 缺失/非法密钥 → 立即失败（`CredentialKeyError`），绝不静默。
- `mask_api_key` 产出可安全展示/落库回显的掩码；明文永不出响应、永不进日志。
"""

from __future__ import annotations

from cryptography.fernet import Fernet, InvalidToken

# 掩码常量：前缀 + 省略号 + 末四位。短 key 用 bullet 占位，绝不泄露字符。
_MASK_PREFIX = "sk-…"
_MASK_PLACEHOLDER = "••••"
_LAST_N = 4


class CredentialKeyError(RuntimeError):
    """加密密钥缺失或非法——快速失败，调用方负责映射为 5xx/配置错误。"""


def _fernet(key: str) -> Fernet:
    if not key:
        raise CredentialKeyError(
            "credential_enc_key 未配置；无法加解密提供商凭据。请设置环境变量。"
        )
    try:
        return Fernet(key.encode())
    except (ValueError, TypeError) as exc:
        # 不回显 key 内容
        raise CredentialKeyError(
            "credential_enc_key 非法（需为 32 字节 url-safe base64 Fernet key）。"
        ) from exc


def encrypt_api_key(plaintext: str, *, key: str) -> bytes:
    """加密明文 API Key，返回可入库的密文字节（`api_key_enc`）。"""
    return _fernet(key).encrypt(plaintext.encode())


def decrypt_api_key(blob: bytes, *, key: str) -> str:
    """解密密文字节回明文。仅供探测/调用使用，绝不进响应或日志。"""
    try:
        return _fernet(key).decrypt(blob).decode()
    except InvalidToken as exc:
        raise CredentialKeyError("凭据密文无法解密（密钥不匹配或数据损坏）。") from exc


def mask_api_key(plaintext: str) -> str:
    """掩码：仅露末四位，如 `sk-…1234`；过短/空则全隐。"""
    if len(plaintext) < _LAST_N:
        return f"{_MASK_PREFIX}{_MASK_PLACEHOLDER}"
    return f"{_MASK_PREFIX}{plaintext[-_LAST_N:]}"
