"""
Storage interfaces for Upstox tokens.
"""

import json
import os
from abc import ABC, abstractmethod
from typing import Dict, Optional

from .exceptions import StorageError


class BaseStorage(ABC):
    """Abstract base class for token storage."""

    @abstractmethod
    def save_token(self, token_data: Dict) -> None:
        """Save token data dictionary."""
        pass

    @abstractmethod
    def get_token(self) -> Optional[Dict]:
        """Retrieve token data dictionary."""
        pass


class FileStorage(BaseStorage):
    """Stores token data in a local JSON file."""

    def __init__(self, file_path: str = "upstox_token.json"):
        self.file_path = file_path

    def save_token(self, token_data: Dict) -> None:
        try:
            # Add fetched_at timestamp for expiry checks (IST)
            import datetime

            import pytz
            ist = pytz.timezone("Asia/Kolkata")
            token_data["fetched_at"] = datetime.datetime.now(ist).isoformat()

            # Ensure parent directory exists
            parent_dir = os.path.dirname(os.path.abspath(self.file_path))
            if parent_dir:
                os.makedirs(parent_dir, exist_ok=True)

            with open(self.file_path, "w", encoding="utf-8") as f:
                json.dump(token_data, f, indent=4)
        except Exception as e:
            raise StorageError(f"Failed to save token to {self.file_path}: {e}")

    def get_token(self) -> Optional[Dict]:
        if not os.path.exists(self.file_path):
            return None
        try:
            with open(self.file_path, "r", encoding="utf-8") as f:
                return json.load(f)
        except Exception as e:
            raise StorageError(f"Failed to read token from {self.file_path}: {e}")


class MemoryStorage(BaseStorage):
    """Temporary in-memory storage (non-persistent)."""

    def __init__(self):
        self._data = None

    def save_token(self, token_data: Dict) -> None:
        import datetime
        import pytz
        ist = pytz.timezone("Asia/Kolkata")
        token_data["fetched_at"] = datetime.datetime.now(ist).isoformat()
        self._data = token_data

    def get_token(self) -> Optional[Dict]:
        return self._data


class EncryptedFileStorage(BaseStorage):
    """Stores token data in an encrypted local file."""

    def __init__(self, file_path: str, encryption_key: str):
        """
        Initialize encrypted storage.

        Args:
            file_path: Path to the encrypted file.
            encryption_key: A 32-byte base64 encoded string (Fernet key).
        """
        from cryptography.fernet import Fernet
        self.file_path = file_path
        self.fernet = Fernet(encryption_key.encode())

    def save_token(self, token_data: Dict) -> None:
        try:
            import datetime
            import pytz
            ist = pytz.timezone("Asia/Kolkata")
            token_data["fetched_at"] = datetime.datetime.now(ist).isoformat()

            # Ensure parent directory exists
            parent_dir = os.path.dirname(os.path.abspath(self.file_path))
            if parent_dir:
                os.makedirs(parent_dir, exist_ok=True)

            json_data = json.dumps(token_data).encode()
            encrypted_data = self.fernet.encrypt(json_data)

            with open(self.file_path, "wb") as f:
                f.write(encrypted_data)
        except Exception as e:
            raise StorageError(f"Failed to save encrypted token: {e}")

    def get_token(self) -> Optional[Dict]:
        if not os.path.exists(self.file_path):
            return None
        try:
            with open(self.file_path, "rb") as f:
                encrypted_data = f.read()

            decrypted_data = self.fernet.decrypt(encrypted_data)
            return json.loads(decrypted_data.decode())
        except Exception as e:
            raise StorageError(f"Failed to read/decrypt token: {e}")

