"""
senzib.utils.security

Небольшие помощники по безопасности:

- HMAC-подпись (например, для вебхуков).
- Проверка подписи с защитой от timing-attack.
- Генератор случайных токенов.
- Шифрование/расшифровка значений через мастер-ключ (Fernet).
"""

from __future__ import annotations

import os
import hmac
import hashlib
import secrets
from typing import Union, Optional

from cryptography.fernet import Fernet, InvalidToken


BytesLike = Union[str, bytes]
_MASTER_KEY_ENV = "MASTER_ENCRYPTION_KEY"


# ===== ВСПОМОГАТЕЛЬНОЕ =====

def _to_bytes(value: BytesLike) -> bytes:
    if isinstance(value, bytes):
        return value
    return str(value).encode("utf-8")


def _get_fernet() -> Fernet:
    """
    Возвращает Fernet-объект на основе мастер-ключа из окружения.

    MASTER_ENCRYPTION_KEY должен быть установлен в env / .env и
    представлять собой строку, сгенерированную Fernet.generate_key().
    """
    key = os.getenv(_MASTER_KEY_ENV)
    if not key:
        raise RuntimeError(
            f"{_MASTER_KEY_ENV} is not set in environment. "
            "Set master key in .env or system env vars."
        )

    if isinstance(key, bytes):
        key = key.decode("utf-8")

    return Fernet(key)


# ===== HMAC-ПОДПИСИ =====

def generate_hmac_signature(
    secret: BytesLike,
    message: BytesLike,
    *,
    algo: str = "sha256",
) -> str:
    """
    Генерирует HMAC-подпись.

    :param secret: секретный ключ (строка или bytes).
    :param message: сообщение (строка или bytes).
    :param algo: алгоритм хеша (sha256, sha1, ...).
    :return: hex-строка подписи.

    Пример:
        sig = generate_hmac_signature(SECRET, body, algo="sha256")
    """
    digestmod = getattr(hashlib, algo)
    return hmac.new(_to_bytes(secret), _to_bytes(message), digestmod).hexdigest()


def verify_hmac_signature(
    secret: BytesLike,
    message: BytesLike,
    signature: BytesLike,
    *,
    algo: str = "sha256",
) -> bool:
    """
    Проверяет HMAC-подпись, используя сравнение, устойчивое к timing-attack.

    :param secret: секретный ключ.
    :param message: сообщение.
    :param signature: ожидаемая подпись (строка или bytes).
    :param algo: алгоритм хеша.
    :return: True, если подпись совпадает, иначе False.
    """
    expected = generate_hmac_signature(secret, message, algo=algo)
    # expected — str (hex), signature может быть str/bytes
    return hmac.compare_digest(expected, _to_bytes(signature).decode("utf-8"))


# ===== СЛУЧАЙНЫЕ ТОКЕНЫ =====

def generate_random_token(length: int = 32) -> str:
    """
    Генерация криптографически стойкого случайного токена.

    :param length: длина токена в байтах (до hex-представления).
    :return: hex-строка токена длиной 2*length.

    Пример:
        token = generate_random_token(16)  # 32 hex-символа
    """
    return secrets.token_hex(length)


# ===== ШИФРОВАНИЕ ЧЕРЕЗ МАСТЕР-КЛЮЧ =====

def encrypt_value(value: Optional[str]) -> Optional[str]:
    """
    Шифрует строку с использованием мастер-ключа (MASTER_ENCRYPTION_KEY).

    :param value: исходная строка (например, client_secret, access_token).
    :return: base64-строка токена, готовая для записи в БД.
    """
    if value is None:
        return None

    f = _get_fernet()
    token = f.encrypt(value.encode("utf-8"))
    return token.decode("utf-8")


def decrypt_value(value: Optional[str]) -> Optional[str]:
    """
    Расшифровывает строку, зашифрованную encrypt_value.

    :param value: base64-строка из БД.
    :return: исходная строка или None.
    :raises InvalidToken: если токен повреждён или ключ не подходит.
    """
    if value is None:
        return None

    f = _get_fernet()
    try:
        plain = f.decrypt(value.encode("utf-8"))
    except InvalidToken as e:
        # здесь можно залогировать через senzib.utils.log, если нужно
        raise e

    return plain.decode("utf-8")


def generate_master_key() -> str:
    """
    Однократная утилита: сгенерировать новый мастер-ключ для установки в env.

    Пример использования:
        from senzib.utils.security import generate_master_key
        print(generate_master_key())

    Затем скопировать вывод в .env:
        MASTER_ENCRYPTION_KEY=...
    """
    return Fernet.generate_key().decode("utf-8")


__all__ = [
    "generate_hmac_signature",
    "verify_hmac_signature",
    "generate_random_token",
    "encrypt_value",
    "decrypt_value",
    "generate_master_key",
]
