"""
OAuth Configuration Management

This module centralizes OAuth-related configuration to eliminate hardcoded values
scattered throughout the codebase. It provides environment variable support and
sensible defaults for all OAuth-related settings.

Supports both OAuth 2.0 and OAuth 2.1 with automatic client capability detection.
"""

import os
from typing import List, Optional, Dict, Any


class OAuthConfig:
    """
    Centralized OAuth configuration management.

    This class eliminates the hardcoded configuration anti-pattern identified
    in the challenge review by providing a single source of truth for all
    OAuth-related configuration values.
    """

    def __init__(self):
        # Base server configuration
        self.base_uri = os.getenv("WORKSPACE_MCP_BASE_URI", "http://localhost")
        self.port = int(os.getenv("PORT", os.getenv("WORKSPACE_MCP_PORT", "8000")))
        self.base_url = f"{self.base_uri}:{self.port}"

        # OAuth client configuration
        self.client_id = os.getenv("GOOGLE_OAUTH_CLIENT_ID")
        self.client_secret = os.getenv("GOOGLE_OAUTH_CLIENT_SECRET")

        # OAuth 2.1 configuration
        self.oauth21_enabled = os.getenv("MCP_ENABLE_OAUTH21", "false").lower() == "true"
        self.pkce_required = self.oauth21_enabled  # PKCE is mandatory in OAuth 2.1
        self.supported_code_challenge_methods = ["S256", "plain"] if not self.oauth21_enabled else ["S256"]

        # Transport mode (will be set at runtime)
        self._transport_mode = "stdio"  # Default

        # Redirect URI configuration
        self.redirect_uri = self._get_redirect_uri()

    def _get_redirect_uri(self) -> str:
        """
        Get the OAuth redirect URI, supporting reverse proxy configurations.

        Returns:
            The configured redirect URI
        """
        explicit_uri = os.getenv("GOOGLE_OAUTH_REDIRECT_URI")
        if explicit_uri:
            return explicit_uri
        return f"{self.base_url}/oauth2callback"

    def get_redirect_uris(self) -> List[str]:
        """
        Get all valid OAuth redirect URIs.

        Returns:
            List of all supported redirect URIs
        """
        uris = []

        # Primary redirect URI
        uris.append(self.redirect_uri)

        # Custom redirect URIs from environment
        custom_uris = os.getenv("OAUTH_CUSTOM_REDIRECT_URIS")
        if custom_uris:
            uris.extend([uri.strip() for uri in custom_uris.split(",")])

        # Remove duplicates while preserving order
        return list(dict.fromkeys(uris))

    def get_allowed_origins(self) -> List[str]:
        """
        Get allowed CORS origins for OAuth endpoints.

        Returns:
            List of allowed origins for CORS
        """
        origins = []

        # Server's own origin
        origins.append(self.base_url)

        # VS Code and development origins
        origins.extend([
            "vscode-webview://",
            "https://vscode.dev",
            "https://github.dev",
        ])

        # Custom origins from environment
        custom_origins = os.getenv("OAUTH_ALLOWED_ORIGINS")
        if custom_origins:
            origins.extend([origin.strip() for origin in custom_origins.split(",")])

        return list(dict.fromkeys(origins))

    def is_configured(self) -> bool:
        """
        Check if OAuth is properly configured.

        Returns:
            True if OAuth client credentials are available
        """
        return bool(self.client_id and self.client_secret)

    def get_oauth_base_url(self) -> str:
        """
        Get OAuth base URL for constructing OAuth endpoints.

        Returns:
            Base URL for OAuth endpoints
        """
        return self.base_url

    def validate_redirect_uri(self, uri: str) -> bool:
        """
        Validate if a redirect URI is allowed.

        Args:
            uri: The redirect URI to validate

        Returns:
            True if the URI is allowed, False otherwise
        """
        allowed_uris = self.get_redirect_uris()
        return uri in allowed_uris

    def get_environment_summary(self) -> dict:
        """
        Get a summary of the current OAuth configuration.

        Returns:
            Dictionary with configuration summary (excluding secrets)
        """
        return {
            "base_url": self.base_url,
            "redirect_uri": self.redirect_uri,
            "client_configured": bool(self.client_id),
            "oauth21_enabled": self.oauth21_enabled,
            "pkce_required": self.pkce_required,
            "transport_mode": self._transport_mode,
            "total_redirect_uris": len(self.get_redirect_uris()),
            "total_allowed_origins": len(self.get_allowed_origins()),
        }

    def set_transport_mode(self, mode: str) -> None:
        """
        Set the current transport mode for OAuth callback handling.

        Args:
            mode: Transport mode ("stdio", "streamable-http", etc.)
        """
        self._transport_mode = mode

    def get_transport_mode(self) -> str:
        """
        Get the current transport mode.

        Returns:
            Current transport mode
        """
        return self._transport_mode

    def is_oauth21_enabled(self) -> bool:
        """
        Check if OAuth 2.1 mode is enabled.

        Returns:
            True if OAuth 2.1 is enabled
        """
        return self.oauth21_enabled

    def detect_oauth_version(self, request_params: Dict[str, Any]) -> str:
        """
        Detect OAuth version based on request parameters.

        This method implements a conservative detection strategy:
        - Only returns "oauth21" when we have clear indicators
        - Defaults to "oauth20" for backward compatibility
        - Respects the global oauth21_enabled flag

        Args:
            request_params: Request parameters from authorization or token request

        Returns:
            "oauth21" or "oauth20" based on detection
        """
        # If OAuth 2.1 is not enabled globally, always return OAuth 2.0
        if not self.oauth21_enabled:
            return "oauth20"

        # Use the structured type for cleaner detection logic
        from auth.oauth_types import OAuthVersionDetectionParams
        params = OAuthVersionDetectionParams.from_request(request_params)

        # Clear OAuth 2.1 indicator: PKCE is present
        if params.has_pkce:
            return "oauth21"

        # Additional detection: Check if we have an active OAuth 2.1 session
        # This is important for tool calls where PKCE params aren't available
        authenticated_user = request_params.get("authenticated_user")
        if authenticated_user:
            try:
                from auth.oauth21_session_store import get_oauth21_session_store
                store = get_oauth21_session_store()
                if store.has_session(authenticated_user):
                    return "oauth21"
            except (ImportError, AttributeError, RuntimeError):
                pass  # Fall back to OAuth 2.0 if session check fails

        # For public clients in OAuth 2.1 mode, we require PKCE
        # But since they didn't send PKCE, fall back to OAuth 2.0
        # This ensures backward compatibility

        # Default to OAuth 2.0 for maximum compatibility
        return "oauth20"

    def get_authorization_server_metadata(self, scopes: Optional[List[str]] = None) -> Dict[str, Any]:
        """
        Get OAuth authorization server metadata per RFC 8414.

        Args:
            scopes: Optional list of supported scopes to include in metadata

        Returns:
            Authorization server metadata dictionary
        """
        metadata = {
            "issuer": self.base_url,
            "authorization_endpoint": f"{self.base_url}/oauth2/authorize",
            "token_endpoint": f"{self.base_url}/oauth2/token",
            "registration_endpoint": f"{self.base_url}/oauth2/register",
            "jwks_uri": "https://www.googleapis.com/oauth2/v3/certs",
            "response_types_supported": ["code", "token"],
            "grant_types_supported": ["authorization_code", "refresh_token"],
            "token_endpoint_auth_methods_supported": ["client_secret_post", "client_secret_basic"],
            "code_challenge_methods_supported": self.supported_code_challenge_methods,
        }

        # Include scopes if provided
        if scopes is not None:
            metadata["scopes_supported"] = scopes

        # Add OAuth 2.1 specific metadata
        if self.oauth21_enabled:
            metadata["pkce_required"] = True
            # OAuth 2.1 deprecates implicit flow
            metadata["response_types_supported"] = ["code"]
            # OAuth 2.1 requires exact redirect URI matching
            metadata["require_exact_redirect_uri"] = True

        return metadata


# Global configuration instance
_oauth_config = None


def get_oauth_config() -> OAuthConfig:
    """
    Get the global OAuth configuration instance.

    Returns:
        The singleton OAuth configuration instance
    """
    global _oauth_config
    if _oauth_config is None:
        _oauth_config = OAuthConfig()
    return _oauth_config


def reload_oauth_config() -> OAuthConfig:
    """
    Reload the OAuth configuration from environment variables.

    This is useful for testing or when environment variables change.

    Returns:
        The reloaded OAuth configuration instance
    """
    global _oauth_config
    _oauth_config = OAuthConfig()
    return _oauth_config


# Convenience functions for backward compatibility
def get_oauth_base_url() -> str:
    """Get OAuth base URL."""
    return get_oauth_config().get_oauth_base_url()


def get_redirect_uris() -> List[str]:
    """Get all valid OAuth redirect URIs."""
    return get_oauth_config().get_redirect_uris()


def get_allowed_origins() -> List[str]:
    """Get allowed CORS origins."""
    return get_oauth_config().get_allowed_origins()


def is_oauth_configured() -> bool:
    """Check if OAuth is properly configured."""
    return get_oauth_config().is_configured()


def set_transport_mode(mode: str) -> None:
    """Set the current transport mode."""
    get_oauth_config().set_transport_mode(mode)


def get_transport_mode() -> str:
    """Get the current transport mode."""
    return get_oauth_config().get_transport_mode()


def is_oauth21_enabled() -> bool:
    """Check if OAuth 2.1 is enabled."""
    return get_oauth_config().is_oauth21_enabled()


def get_oauth_redirect_uri() -> str:
    """Get the primary OAuth redirect URI."""
    return get_oauth_config().redirect_uri