# -*- coding: utf-8 -*-
"""
crypto.py — SSH 凭据对称加密（Fernet）

密钥来源优先级：
  1. 环境变量 MONITOR_ENC_KEY（标准 Fernet key）
  2. 派生自 SECRET_KEY（SHA-256 → base64 → Fernet key）
  3. 均缺失时派生自固定兜底串（仅开发环境，告警）

设计目标：SSH 密码禁止明文落盘，targets.json 只存密文 token。
模块自包含，不依赖全局 utils/logger，保持隔离。
"""

import os
import base64
import hashlib
import logging

from cryptography.fernet import Fernet, InvalidToken

logger = logging.getLogger("service_monitor.crypto")
if not logger.handlers:
    _h = logging.StreamHandler()
    _h.setFormatter(logging.Formatter("[%(levelname)s] %(name)s: %(message)s"))
    logger.addHandler(_h)
logger.setLevel(logging.INFO)

# 派生兜底串（仅当 MONITOR_ENC_KEY 与 SECRET_KEY 都缺失时使用）
_FALLBACK_SEED = "service-monitor-dev-fallback-seed"

# 加密标记前缀，便于识别密文
_ENC_PREFIX = "enc:"


def _derive_key(seed: str) -> bytes:
    """用 SHA-256 将任意字符串派生为合法 Fernet key（32 字节 → urlsafe base64）。"""
    digest = hashlib.sha256(seed.encode("utf-8")).digest()
    return base64.urlsafe_b64encode(digest)


def _get_key() -> bytes:
    """按优先级解析 Fernet 密钥。"""
    env_key = os.environ.get("MONITOR_ENC_KEY")
    if env_key:
        try:
            Fernet(env_key.encode("utf-8"))
            return env_key.encode("utf-8")
        except (ValueError, TypeError):
            return _derive_key(env_key)

    secret = os.environ.get("SECRET_KEY")
    if secret:
        return _derive_key(secret)

    logger.warning(
        "MONITOR_ENC_KEY 与 SECRET_KEY 均未设置，凭据加密使用开发兜底密钥。"
        "生产环境请设置 MONITOR_ENC_KEY。"
    )
    return _derive_key(_FALLBACK_SEED)


def get_fernet() -> Fernet:
    """获取 Fernet 实例。"""
    return Fernet(_get_key())


def encrypt_password(plain: str) -> str:
    """加密明文密码，返回带前缀的 token；空串返回空串。"""
    if not plain:
        return ""
    token = get_fernet().encrypt(plain.encode("utf-8")).decode("utf-8")
    return _ENC_PREFIX + token


def decrypt_password(token: str) -> str:
    """解密 token 还原明文；空串返回空串。

    兼容性：无前缀的值视为历史明文直接返回（并告警）。
    解密失败抛 ValueError。
    """
    if not token:
        return ""
    if not token.startswith(_ENC_PREFIX):
        logger.warning("检测到未加密的凭据值，建议重新保存以加密。")
        return token
    raw = token[len(_ENC_PREFIX):]
    try:
        return get_fernet().decrypt(raw.encode("utf-8")).decode("utf-8")
    except InvalidToken as exc:
        raise ValueError("凭据解密失败：密钥不匹配或数据损坏") from exc


def is_encrypted(value: str) -> bool:
    """判断值是否为已加密 token。"""
    return bool(value) and value.startswith(_ENC_PREFIX)
