from __future__ import annotations

import base64
import hashlib

from cryptography.fernet import Fernet, InvalidToken

from app.core.config import get_settings


ENCRYPTED_PREFIX = "enc:v1:"
MASKED_SECRET = "******"
SENSITIVE_CONFIG_KEYS = {
    "aliyun_sms_access_key_secret",
    "aliyun_nls_access_key_secret",
    "feishu_app_secret",
}


class SecretService:
    @staticmethod
    def encrypt(value: str) -> str:
        if not value or value.startswith(ENCRYPTED_PREFIX):
            return value
        token = SecretService._fernet().encrypt(value.encode("utf-8")).decode("ascii")
        return f"{ENCRYPTED_PREFIX}{token}"

    @staticmethod
    def decrypt(value: str | None) -> str:
        if not value:
            return ""
        if not value.startswith(ENCRYPTED_PREFIX):
            return value
        try:
            return SecretService._fernet().decrypt(value[len(ENCRYPTED_PREFIX) :].encode("ascii")).decode("utf-8")
        except InvalidToken as exc:
            raise RuntimeError("敏感配置无法解密，请检查 CONFIG_ENCRYPTION_KEY 是否与保存时一致") from exc

    @staticmethod
    def masked(value: str | None) -> str:
        return MASKED_SECRET if value else ""

    @staticmethod
    def validate_production_key() -> None:
        settings = get_settings()
        if settings.app_env.strip().lower() in {"local", "dev", "development", "docker", "test", "testing"}:
            return
        if not settings.config_encryption_key.strip():
            raise RuntimeError("生产环境必须配置 CONFIG_ENCRYPTION_KEY")
        SecretService._fernet()

    @staticmethod
    def _fernet() -> Fernet:
        settings = get_settings()
        configured = settings.config_encryption_key.strip()
        if configured:
            try:
                return Fernet(configured.encode("ascii"))
            except (ValueError, TypeError) as exc:
                raise RuntimeError("CONFIG_ENCRYPTION_KEY 格式无效，必须是 Fernet 密钥") from exc
        fallback = base64.urlsafe_b64encode(hashlib.sha256(settings.jwt_secret_key.encode("utf-8")).digest())
        return Fernet(fallback)
