"""Cryptography module for symmetric encryption"""

import os
from typing import Optional
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
import base64


ENV_WEB_SECRET: str = "DIONA_WEB_SECRET"
DEFAULT_SECRET: str = "0" * 32


class CryptoManager:
    """Singleton class for managing symmetric encryption"""

    _instance: Optional["CryptoManager"] = None

    def __new__(cls) -> "CryptoManager":
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance

    def __init__(self) -> None:
        if self._initialized:
            return

        self._key = self._load_key()
        self._initialized = True

    def _load_key(self) -> bytes:
        """Load encryption key from environment variable"""
        from diona.project.environment import get_environment_manager

        env_mgr = get_environment_manager()
        secret = env_mgr.get(ENV_WEB_SECRET)

        if secret is None:
            secret = os.environ.get(ENV_WEB_SECRET, DEFAULT_SECRET)

        key = secret.encode("utf-8")
        if len(key) < 32:
            key = key.ljust(32, b"0")
        elif len(key) > 32:
            key = key[:32]

        return key

    def encrypt(self, plaintext: str) -> str:
        """Encrypt plaintext using AES-256-CBC

        Args:
            plaintext: Text to encrypt

        Returns:
            Base64 encoded encrypted text
        """
        iv = os.urandom(16)
        cipher = Cipher(
            algorithms.AES(self._key),
            modes.CBC(iv),
            backend=default_backend()
        )
        encryptor = cipher.encryptor()

        padded = self._pkcs7_pad(plaintext.encode("utf-8"))
        ciphertext = encryptor.update(padded) + encryptor.finalize()

        result = base64.b64encode(iv + ciphertext).decode("utf-8")
        return result

    def decrypt(self, encrypted: str) -> str:
        """Decrypt encrypted text using AES-256-CBC

        Args:
            encrypted: Base64 encoded encrypted text

        Returns:
            Decrypted plaintext
        """
        try:
            data = base64.b64decode(encrypted.encode("utf-8"))
            iv = data[:16]
            ciphertext = data[16:]

            cipher = Cipher(
                algorithms.AES(self._key),
                modes.CBC(iv),
                backend=default_backend()
            )
            decryptor = cipher.decryptor()

            padded = decryptor.update(ciphertext) + decryptor.finalize()
            plaintext = self._pkcs7_unpad(padded).decode("utf-8")

            return plaintext
        except Exception as e:
            raise ValueError(f"Decryption failed: {str(e)}")

    @staticmethod
    def _pkcs7_pad(data: bytes) -> bytes:
        """Apply PKCS7 padding"""
        padding_length = 16 - (len(data) % 16)
        padding = bytes([padding_length] * padding_length)
        return data + padding

    @staticmethod
    def _pkcs7_unpad(data: bytes) -> bytes:
        """Remove PKCS7 padding"""
        padding_length = data[-1]
        return data[:-padding_length]


def get_crypto_manager() -> CryptoManager:
    """Get singleton crypto manager instance

    Returns:
        CryptoManager instance
    """
    return CryptoManager()
