import os

import redis
from cryptography.fernet import Fernet
from dotenv import dotenv_values

from routers import settings


def get_local_env_file():
    """
    Get the path to the local .env file.
    """
    current_file_path = os.path.abspath(__file__)
    current_directory = os.path.dirname(current_file_path)
    return os.path.join(current_directory, ".env")


def read_dotenv_values(file_path=None):
    """
    Load the local .env file.
    """
    if file_path is None:
        dotenv_path = get_local_env_file()
    else:
        dotenv_path = file_path
    if os.path.exists(dotenv_path):
        config = dotenv_values(dotenv_path)
        return config
    raise FileNotFoundError(f"No .env file found at {dotenv_path}")


class EncryptedRedis:
    """
    A wrapper around Redis that encrypts and decrypts values.
    """

    def __init__(self):
        # Connect to Redis
        self.redis = redis.Redis(
            host=settings.REDIS_HOST,
            port=settings.REDIS_PORT,
            db=settings.REDIS_DB,
            username=settings.REDIS_USER,
            password=settings.REDIS_PASSWORD,
            ssl=True,
        )
        # Initialize the cipher with the key
        self.cipher = Fernet(settings.ROUTERS_REDIS_KEY)

    def encrypt(self, data: str) -> bytes:
        """Encrypt a string value"""
        return self.cipher.encrypt(data.encode())

    def decrypt(self, data: bytes) -> str:
        """Decrypt bytes back to a string"""
        return self.cipher.decrypt(data).decode()

    def set_encrypted(self, key: str, value: str) -> bool:
        """
        Store an encrypted value in Redis.

        Args:
            key: Redis key
            value: String value to encrypt and store

        Returns:
            bool: True if successful
        """
        return self.redis.set(key, self.encrypt(value))

    def get_decrypted(self, key: str) -> str | None:
        """
        Retrieve and decrypt a value from Redis.

        Args:
            key: Redis key

        Returns:
            Decrypted string value if found, None if key doesn't exist
        """
        encrypted_data = self.redis.get(key)
        if encrypted_data:
            return self.decrypt(encrypted_data)
        return None

    def list_keys(self, namespace_pattern: str) -> list[bytes]:
        """
        List all keys matching a pattern.

        Args:
            namespace_pattern: Pattern to match (e.g. "routers:*")

        Returns:
            List of matching keys as bytes
        """
        return list(self.redis.scan_iter(match=namespace_pattern))

    def delete(self, key: str) -> bool:
        """
        Delete a key from Redis.

        Args:
            key: Key to delete

        Returns:
            bool: True if key was deleted, False if key didn't exist
        """
        return bool(self.redis.delete(key))
