"""Token storage and management for Maeris MCP."""

import json
import os
from datetime import datetime
from pathlib import Path
from typing import Any

import jwt
import structlog

logger = structlog.get_logger(__name__)


class TokenStore:
    """Manage Firebase ID token storage and retrieval."""

    def __init__(self, config_dir: Path | None = None) -> None:
        """Initialize token store.

        Args:
            config_dir: Custom config directory. Falls back to MAERIS_CONFIG_DIR
                        env var (set by .mcp.json), then ~/.maeris as last resort.
        """
        if config_dir:
            self.config_dir = config_dir
        elif env_dir := os.getenv("MAERIS_CONFIG_DIR"):
            self.config_dir = Path(env_dir)
        else:
            self.config_dir = Path.home() / ".maeris"
        self.credentials_file = self.config_dir / "credentials"

    def save_token(
        self,
        access_token: str,
        refresh_token: str | None = None,
        token_type: str = "Bearer",
    ) -> None:
        """Save Firebase ID token and extract claims.

        Args:
            access_token: Firebase ID token (JWT)
            refresh_token: Optional refresh token
            token_type: Token type (default: Bearer)
        """
        # Decode JWT to extract claims (without verification - just for reading)
        try:
            claims = jwt.decode(
                access_token,
                options={"verify_signature": False},  # We're just reading claims
            )
        except jwt.DecodeError as e:
            logger.error("failed_to_decode_token", error=str(e))
            raise ValueError(f"Invalid JWT token: {e}")

        # Extract expiration
        exp_timestamp = claims.get("exp")
        expires_at = (
            datetime.fromtimestamp(exp_timestamp).isoformat() if exp_timestamp else None
        )

        # Build token data
        token_data = {
            "access_token": access_token,
            "token_type": token_type,
            "expires_at": expires_at,
            # Firebase ID token standard claims
            "user_id": claims.get("sub"),  # Firebase user ID
            "email": claims.get("email"),
            "email_verified": claims.get("email_verified", False),
            # Custom claims (set by backend via Firebase Admin SDK)
            "app_id": claims.get("app_id"),  # Application ID
            "app_name": claims.get("app_name"),  # Application name
        }

        # Add refresh token if provided
        if refresh_token:
            token_data["refresh_token"] = refresh_token

        # Save to file
        self._save_to_file(token_data)
        logger.info(
            "token_saved",
            user_id=token_data.get("user_id"),
            email=token_data.get("email"),
            app_id=token_data.get("app_id"),
            expires_at=expires_at,
        )

    def load_token(self) -> str | None:
        """Load access token from storage.

        Returns:
            Access token string or None if not found
        """
        data = self._load_from_file()
        return data.get("access_token") if data else None

    def get_token_data(self) -> dict[str, Any] | None:
        """Get full token data including claims.

        Returns:
            Dict with token and claims or None if not found
        """
        return self._load_from_file()

    def get_user_id(self) -> str | None:
        """Get Firebase user ID from stored token.

        Returns:
            User ID (Firebase UID) or None
        """
        data = self._load_from_file()
        return data.get("user_id") if data else None

    def get_email(self) -> str | None:
        """Get user email from stored token.

        Returns:
            Email address or None
        """
        data = self._load_from_file()
        return data.get("email") if data else None

    def get_app_id(self) -> str | None:
        """Get application ID from stored token custom claims.

        Returns:
            Application ID or None
        """
        data = self._load_from_file()
        return data.get("app_id") if data else None

    def get_app_name(self) -> str | None:
        """Get application name from stored token custom claims.

        Returns:
            Application name or None
        """
        data = self._load_from_file()
        return data.get("app_name") if data else None

    def is_authenticated(self) -> bool:
        """Check if user is authenticated (has valid token).

        Returns:
            True if token exists and is not expired
        """
        if not self.credentials_file.exists():
            return False

        data = self._load_from_file()
        if not data or "access_token" not in data:
            return False

        # Check if token is expired
        return not self.is_token_expired()

    def is_token_expired(self) -> bool:
        """Check if stored token is expired.

        Returns:
            True if token is expired or expiration unknown
        """
        data = self._load_from_file()
        if not data or "expires_at" not in data:
            return True

        try:
            expires_at = datetime.fromisoformat(data["expires_at"])
            return datetime.now() >= expires_at
        except (ValueError, TypeError):
            return True

    def save_app_id(self, app_id: str, app_name: str = "") -> None:
        """Update app_id and app_name in the credentials file without touching other fields."""
        data = self._load_from_file() or {}
        data["app_id"] = app_id
        data["app_name"] = app_name
        self._save_to_file(data)
        logger.info("app_id_saved", app_id=app_id, app_name=app_name)

    def save_environment(self, environment_id: str, environment_name: str = "") -> None:
        """Update selected environment in credentials without touching other fields."""
        data = self._load_from_file() or {}
        data["environment_id"] = environment_id
        data["environment_name"] = environment_name
        self._save_to_file(data)
        logger.info(
            "environment_saved",
            environment_id=environment_id,
            environment_name=environment_name,
        )

    def get_environment_id(self) -> str | None:
        """Get selected environment ID from credentials."""
        data = self._load_from_file()
        return data.get("environment_id") if data else None

    def get_environment_name(self) -> str | None:
        """Get selected environment name from credentials."""
        data = self._load_from_file()
        return data.get("environment_name") if data else None

    def clear(self) -> None:
        """Remove stored credentials."""
        if self.credentials_file.exists():
            self.credentials_file.unlink()
            logger.info("credentials_cleared")

    def _load_from_file(self) -> dict[str, Any] | None:
        """Load token data from credentials file.

        Returns:
            Token data dict or None if file doesn't exist
        """
        if not self.credentials_file.exists():
            return None

        try:
            content = self.credentials_file.read_text()
            return json.loads(content)
        except (json.JSONDecodeError, OSError) as e:
            logger.error("failed_to_load_credentials", error=str(e))
            return None

    def _save_to_file(self, data: dict[str, Any]) -> None:
        """Save token data to credentials file.

        Args:
            data: Token data to save
        """
        # Create config directory if it doesn't exist
        self.config_dir.mkdir(parents=True, exist_ok=True)

        # Write credentials file
        self.credentials_file.write_text(json.dumps(data, indent=2))

        # Set file permissions to owner read/write only (Unix-like systems)
        try:
            self.credentials_file.chmod(0o600)
        except (OSError, NotImplementedError):
            # Windows doesn't support chmod in the same way
            logger.warning("could_not_set_file_permissions")


class AuthenticationError(Exception):
    """Raised when authentication fails or token is invalid."""

    pass
