"""
Persistent token storage for Xero OAuth2 tokens.
Stores tokens in a JSON file in a Docker volume for persistence across container restarts.
"""
import json
import os
import logging
from pathlib import Path
from typing import Optional, Dict
from dotenv import load_dotenv

logger = logging.getLogger(__name__)

_root = Path(__file__).resolve().parent.parent.parent.parent
_env = _root / ".env"
if _env.exists():
    load_dotenv(_env)
else:
    load_dotenv()

# Token storage path - use Docker volume if available, otherwise fallback to local
_default_dir = os.getenv("XERO_TOKEN_STORAGE_DIR", "/app/data/tokens")
if not Path("/app").exists() and _default_dir.startswith("/app"):
    _default_dir = str(Path(__file__).resolve().parent.parent / "tokens")
TOKEN_STORAGE_DIR = Path(_default_dir)
TOKEN_STORAGE_FILE = TOKEN_STORAGE_DIR / "xero_tokens.json"

# Ensure directory exists (don't fail import on permission errors)
try:
    TOKEN_STORAGE_DIR.mkdir(parents=True, exist_ok=True)
except (PermissionError, OSError) as e:
    logger.warning(f"Could not create token storage directory {TOKEN_STORAGE_DIR}: {e}")


class TokenStorage:
    """Manages persistent storage of Xero OAuth2 tokens."""
    
    def __init__(self, storage_file: Path = TOKEN_STORAGE_FILE):
        self.storage_file = storage_file
        self._ensure_storage_file()
    
    def _ensure_storage_file(self):
        """Ensure the storage file exists with default structure."""
        if not self.storage_file.exists():
            self._write_tokens({
                "refresh_token": None,
                "access_token": None,
                "token_refreshed_at": None,
                "expires_at": None
            })
    
    def _read_tokens(self) -> Dict:
        """Read tokens from storage file."""
        try:
            if self.storage_file.exists():
                with open(self.storage_file, 'r') as f:
                    return json.load(f)
        except (json.JSONDecodeError, IOError) as e:
            logger.warning(f"Error reading token storage: {e}. Using defaults.")
        
        return {
            "refresh_token": None,
            "access_token": None,
            "token_refreshed_at": None,
            "expires_at": None
        }
    
    def _write_tokens(self, tokens: Dict):
        """Write tokens to storage file."""
        try:
            # Ensure directory exists before writing
            self.storage_file.parent.mkdir(parents=True, exist_ok=True)
            # Write to temporary file first, then rename (atomic operation)
            temp_file = self.storage_file.with_suffix('.tmp')
            with open(temp_file, 'w') as f:
                json.dump(tokens, f, indent=2)
            temp_file.replace(self.storage_file)
            logger.debug(f"Tokens written to {self.storage_file}")
        except IOError as e:
            logger.error(f"Error writing token storage: {e}")
            raise
    
    def get_refresh_token(self) -> Optional[str]:
        """
        Get the stored refresh token.
        Falls back to environment variable if not in storage.
        """
        tokens = self._read_tokens()
        refresh_token = tokens.get("refresh_token")
        
        # Fallback to environment variable if not in storage
        if not refresh_token:
            refresh_token = os.getenv("REFRESH_TOKEN")
            if refresh_token:
                # Save it to storage for future use
                self.save_refresh_token(refresh_token)
        
        return refresh_token
    
    def save_refresh_token(self, refresh_token: str):
        """Save refresh token to persistent storage."""
        tokens = self._read_tokens()
        tokens["refresh_token"] = refresh_token
        self._write_tokens(tokens)
        logger.info("Refresh token saved to persistent storage")
    
    def save_access_token(self, access_token: str, expires_in: int = 1800):
        """
        Save access token with expiration time.
        
        Args:
            access_token: The access token
            expires_in: Token expiration time in seconds (default 1800 = 30 minutes)
        """
        import time
        tokens = self._read_tokens()
        tokens["access_token"] = access_token
        tokens["token_refreshed_at"] = time.time()
        tokens["expires_at"] = time.time() + expires_in
        self._write_tokens(tokens)
        logger.debug("Access token saved to persistent storage")
    
    def get_access_token(self) -> Optional[str]:
        """Get cached access token if still valid."""
        tokens = self._read_tokens()
        access_token = tokens.get("access_token")
        expires_at = tokens.get("expires_at")
        
        # Check if token is still valid (with 5 minute buffer)
        if access_token and expires_at:
            import time
            if time.time() < (expires_at - 300):  # 5 minute buffer
                return access_token
        
        return None
    
    def clear_tokens(self):
        """Clear all stored tokens (for re-authentication)."""
        self._write_tokens({
            "refresh_token": None,
            "access_token": None,
            "token_refreshed_at": None,
            "expires_at": None
        })
        logger.info("Tokens cleared from storage")


# Global instance
_token_storage = None

def get_token_storage() -> TokenStorage:
    """Get the global token storage instance."""
    global _token_storage
    if _token_storage is None:
        _token_storage = TokenStorage()
    return _token_storage
