"""
Encryption/Decryption Utility Module
Provides secure encryption and decryption for sensitive data storage
Uses base64 encoding with salt-based obfuscation
"""

import base64
import hashlib
import json
from typing import Any, Optional
from ws_package.configs.security import Security_config
from ws_package.log.writer import LogWriter
from ws_package.exceptions.custom_exceptions import EncryptionError

logger = LogWriter.file(__name__)


class EncryptionUtil:
    """
    Utility class for encrypting and decrypting sensitive data
    Uses the same encryption pattern as the existing codebase
    """
    
    def __init__(self):
        """Initialize encryption utility with salt from security config"""
        self.salt = Security_config('encryption_salt', 'Security')
        if not self.salt:
            logger.warning("Encryption salt not found in security.ini - encryption may fail")
            self.salt = ""
    
    def encrypt(self, plaintext: str) -> str:
        """
        Encrypt plaintext string
        Uses the same pattern as existing codebase: base64 encode -> reverse -> add salt
        
        Args:
            plaintext: The string to encrypt
            
        Returns:
            Encrypted string with salt prefix (format: salt$reversed_base64)
            
        Raises:
            EncryptionError: If encryption fails
        """
        if not plaintext or not isinstance(plaintext, str):
            raise EncryptionError("Plaintext must be a non-empty string")
        
        try:
            # Encode to base64
            encoded = base64.b64encode(plaintext.encode('utf-8')).decode('utf-8')
            
            # Reverse the string (simple obfuscation) - matches existing pattern
            reversed_encoded = encoded[::-1]
            
            # Add salt prefix
            # Salt format from security.ini should be like "9XXXXXXX$"
            # If salt doesn't contain '$', add it
            if self.salt:
                if '$' not in self.salt:
                    salt_prefix = f"{self.salt}$"
                else:
                    salt_prefix = self.salt
            else:
                salt_prefix = "$"
            
            encrypted = f"{salt_prefix}{reversed_encoded}"
            
            return encrypted
            
        except Exception as e:
            logger.error("Error encrypting data: %s", e)
            raise EncryptionError(f"Encryption failed: {e}")
    
    def decrypt(self, encrypted_text: str) -> str:
        """
        Decrypt encrypted string
        
        Args:
            encrypted_text: The encrypted string to decrypt
            
        Returns:
            Decrypted plaintext string
            
        Raises:
            EncryptionError: If decryption fails
        """
        if not encrypted_text or not isinstance(encrypted_text, str):
            raise EncryptionError("Encrypted text must be a non-empty string")
        
        try:
            # Find salt delimiter
            position_of_dollar = encrypted_text.find('$')
            if position_of_dollar == -1:
                raise EncryptionError("Invalid encrypted format: missing '$' delimiter")
            
            # Extract data after salt
            encoded_data = encrypted_text[position_of_dollar + 1:]
            if not encoded_data:
                raise EncryptionError("Invalid encrypted format: empty data after salt")
            
            # Reverse the string (undo obfuscation)
            reversed_encoded = encoded_data[::-1]
            
            # Decode from base64
            decrypted = base64.b64decode(reversed_encoded).decode('utf-8')
            
            return decrypted
            
        except ValueError as e:
            logger.error("Value error in decryption: %s", e)
            raise EncryptionError(f"Invalid base64 data: {e}")
        except UnicodeDecodeError as e:
            logger.error("Unicode decode error in decryption: %s", e)
            raise EncryptionError(f"Invalid UTF-8 data: {e}")
        except Exception as e:
            logger.error("Unexpected error in decryption: %s", e)
            raise EncryptionError(f"Decryption failed: {e}")


# Global instance
_encryption_util: Optional[EncryptionUtil] = None


def get_encryption_util() -> EncryptionUtil:
    """Get or create global encryption utility instance"""
    global _encryption_util
    if _encryption_util is None:
        _encryption_util = EncryptionUtil()
    return _encryption_util


def encrypt_data(plaintext: str) -> str:
    """Convenience function to encrypt data"""
    return get_encryption_util().encrypt(plaintext)


def decrypt_data(encrypted_text: str) -> str:
    """Convenience function to decrypt data"""
    return get_encryption_util().decrypt(encrypted_text)


def decrypt_aes_gcm_api_payload(encrypted_data: str, secret_key: str) -> Optional[str]:
    """Decrypt AES-256-GCM payloads from the data/key API (PHP-compatible layout)."""
    if not encrypted_data or not secret_key:
        return None
    try:
        from cryptography.hazmat.primitives.ciphers.aead import AESGCM

        key = hashlib.sha256(secret_key.encode("utf-8")).digest()
        raw = base64.b64decode(encrypted_data)
        iv = raw[:12]
        tag = raw[12:28]
        ciphertext = raw[28:]
        aesgcm = AESGCM(key)
        return aesgcm.decrypt(iv, ciphertext + tag, None).decode("utf-8")
    except Exception as e:
        logger.error("AES-GCM API payload decryption failed: %s", e)
        return None


def decrypt_data_key_api_payload(encrypted_data: str) -> Optional[dict[str, Any]]:
    """Decrypt and parse JSON credentials from data/key API encrypted `response.data`."""
    secret_key = Security_config("data_key_secret", "Security")
    if not secret_key:
        logger.error("data_key_secret missing from security.ini — cannot decrypt data/key payload")
        return None
    plaintext = decrypt_aes_gcm_api_payload(encrypted_data, secret_key)
    if not plaintext:
        return None
    try:
        parsed = json.loads(plaintext)
        return parsed if isinstance(parsed, dict) else None
    except json.JSONDecodeError as e:
        logger.error("data/key decrypted payload is not valid JSON: %s", e)
        return None

