"""
Async MCP server for LinkedIn post creation with optional OAuth support.

Provides tools for creating, managing, and optimizing LinkedIn posts using
a design system approach with components, themes, and variants.

OAuth Support:
    To enable OAuth, set these environment variables:
    - LINKEDIN_CLIENT_ID: LinkedIn app client ID
    - LINKEDIN_CLIENT_SECRET: LinkedIn app client secret
    - LINKEDIN_REDIRECT_URI: OAuth callback URL (default: http://localhost:8000/oauth/callback)
    - OAUTH_SERVER_URL: OAuth server base URL (default: http://localhost:8000)
    - OAUTH_ENABLED: Enable OAuth (default: true if credentials present)

    Note: Uses generic OAuth implementation from chuk-mcp-server.
"""

import os
from typing import Any, Optional

from chuk_mcp_server import ChukMCPServer

from .api import LinkedInClient
from .manager import LinkedInManager
from .manager_factory import ManagerFactory, set_factory
from .tools.composition_tools import register_composition_tools
from .tools.draft_tools import register_draft_tools
from .tools.publishing_tools import register_publishing_tools
from .tools.registry_tools import register_registry_tools
from .tools.theme_tools import register_theme_tools

# Initialize the MCP server with OAuth provider getter
mcp = ChukMCPServer("chuk-mcp-linkedin")

# Initialize manager factory (creates per-user managers)
# Use artifacts by default, configure storage backend via env vars
manager_factory = ManagerFactory(
    use_artifacts=True,
    artifact_provider=os.getenv("ARTIFACT_PROVIDER", "memory"),
)
set_factory(manager_factory)

# Legacy: Keep a single manager for backward compatibility (will be deprecated)
manager = LinkedInManager()
linkedin_client = LinkedInClient()

# Set OAuth provider getter in the protocol handler (will be populated after setup_oauth)
mcp.protocol.oauth_provider_getter = lambda: get_oauth_provider()

# Global OAuth provider (will be set if OAuth is enabled)
oauth_provider = None

# Global token store - shared across all OAuth operations
# This ensures tokens stored in one context are visible in another
# TODO: Remove when chuk-sessions ships shared_memory provider
_global_token_store = None

# Register tools with the server (tools will use factory internally)
draft_tools = register_draft_tools(mcp)
composition_tools = register_composition_tools(mcp)
theme_tools = register_theme_tools(mcp)
registry_tools = register_registry_tools(mcp)
publishing_tools = register_publishing_tools(mcp, linkedin_client)

# ============================================================================
# OAuth Integration (Optional)
# ============================================================================


def setup_preview_routes() -> None:
    """Set up preview routes for serving HTML previews."""
    from chuk_mcp_server.endpoint_registry import http_endpoint_registry
    from starlette.requests import Request
    from starlette.responses import HTMLResponse, JSONResponse

    async def serve_preview(request: Request) -> HTMLResponse | JSONResponse:
        """Serve HTML preview for a draft using shareable preview token."""
        preview_token = request.path_params.get("preview_token")

        if not preview_token:
            return JSONResponse({"error": "preview_token required"}, status_code=400)

        # Search across all active user managers for the draft with this token
        try:
            from .manager_factory import get_factory

            factory = get_factory()
            active_users = factory.get_active_users()

            # Search through all users' managers
            for user_id in active_users:
                user_manager = factory.get_manager(user_id)
                draft = user_manager.get_draft_by_preview_token(preview_token)

                if draft:
                    # Found the draft, generate/retrieve preview
                    html_content = await user_manager.read_preview_html_async(draft.draft_id)

                    if not html_content:
                        return JSONResponse(
                            {"error": "Failed to generate preview"}, status_code=500
                        )

                    return HTMLResponse(content=html_content)

            # Token not found in any user's drafts
            return JSONResponse(
                {"error": "Preview not found. The draft may have been deleted."}, status_code=404
            )

        except Exception as e:
            return JSONResponse({"error": f"Failed to serve preview: {str(e)}"}, status_code=500)

    # Register route using the endpoint registry (called before app is created)
    http_endpoint_registry.register_endpoint(
        path="/preview/{preview_token}",
        handler=serve_preview,
        methods=["GET"],
        name="preview_draft",
        description="Preview a draft post in HTML format using shareable token",
    )


# ============================================================================
# HTTP Server Setup (Preview Routes + OAuth)
# ============================================================================


def setup_http_server() -> Optional[Any]:
    """Set up HTTP server features: preview routes and optional OAuth."""
    # Always setup preview routes for HTTP mode
    setup_preview_routes()

    # Setup OAuth if credentials are available
    return setup_oauth()


def setup_oauth() -> Optional[Any]:
    """Set up OAuth middleware if credentials are available."""
    global oauth_provider, _global_token_store

    OAUTH_ENABLED = os.getenv("OAUTH_ENABLED", "true").lower() == "true"
    OAUTH_MODE = os.getenv("OAUTH_MODE", "linkedin").lower()  # linkedin or keycloak

    if not OAUTH_ENABLED:
        return None

    # Import generic OAuth middleware from chuk-mcp-server
    from chuk_mcp_server.oauth import OAuthMiddleware, TokenStore

    OAUTH_SERVER_URL = os.getenv("OAUTH_SERVER_URL", "http://localhost:8000")

    # Create shared token store (used by both modes)
    if _global_token_store is None:
        _global_token_store = TokenStore(sandbox_id="chuk-mcp-linkedin")
        print("✓ Created shared token store for OAuth")

    # ============================================================================
    # Keycloak Mode
    # ============================================================================
    if OAUTH_MODE == "keycloak":
        KEYCLOAK_BASE_URL = os.getenv("KEYCLOAK_BASE_URL")
        KEYCLOAK_REALM = os.getenv("KEYCLOAK_REALM")
        KEYCLOAK_PROVIDER_ALIAS = os.getenv("KEYCLOAK_PROVIDER_ALIAS", "linkedin")

        if not KEYCLOAK_BASE_URL or not KEYCLOAK_REALM:
            print("⚠ Keycloak mode enabled but configuration missing")
            print("  Set KEYCLOAK_BASE_URL and KEYCLOAK_REALM")
            return None

        # Import Keycloak provider
        from .oauth.keycloak_provider import KeycloakOAuthProvider

        # Create Keycloak OAuth provider
        oauth_provider = KeycloakOAuthProvider(
            keycloak_base_url=KEYCLOAK_BASE_URL,
            realm_name=KEYCLOAK_REALM,
            provider_alias=KEYCLOAK_PROVIDER_ALIAS,
            oauth_server_url=OAUTH_SERVER_URL,
            token_store=_global_token_store,
        )

        # Initialize OAuth middleware with Keycloak provider
        oauth_middleware = OAuthMiddleware(
            mcp_server=mcp,
            provider=oauth_provider,
            oauth_server_url=OAUTH_SERVER_URL,
            callback_path="/oauth/callback",  # Dummy path - Keycloak handles actual OAuth flow
            scopes_supported=[
                "linkedin.posts",
                "linkedin.profile",
                "linkedin.documents",
            ],
            service_documentation="https://github.com/chrishayuk/chuk-mcp-linkedin",
            provider_name="Keycloak (LinkedIn)",
        )

        # Override the protected resource metadata endpoint for Keycloak mode
        # Register AFTER middleware to override the default endpoint
        @mcp.endpoint("/.well-known/oauth-protected-resource", methods=["GET"])
        async def keycloak_protected_resource_metadata(request: Any) -> Any:
            """OAuth Protected Resource Metadata endpoint - points to Keycloak."""
            from starlette.responses import JSONResponse

            metadata = oauth_provider.get_protected_resource_metadata()  # type: ignore[union-attr]
            return JSONResponse(metadata, headers={"Access-Control-Allow-Origin": "*"})

        print("✓ OAuth enabled - Keycloak mode")
        print(f"  MCP Resource Server: {OAUTH_SERVER_URL}")
        print(f"  Keycloak Authorization Server: {KEYCLOAK_BASE_URL}/realms/{KEYCLOAK_REALM}")
        print(f"  Protected Resource: {OAUTH_SERVER_URL}/.well-known/oauth-protected-resource")
        print(f"  LinkedIn Provider Alias: {KEYCLOAK_PROVIDER_ALIAS}")
        print()
        print("  ⚠️  Important Keycloak Configuration:")
        print("     1. Enable 'Store Tokens' in LinkedIn Identity Provider settings")
        print("     2. Add 'broker -> read-token' role to users")
        print("     3. Configure LinkedIn as Identity Provider in Keycloak")

        return oauth_middleware

    # ============================================================================
    # LinkedIn Direct Mode (Default)
    # ============================================================================
    elif OAUTH_MODE == "linkedin":
        LINKEDIN_CLIENT_ID = os.getenv("LINKEDIN_CLIENT_ID")
        LINKEDIN_CLIENT_SECRET = os.getenv("LINKEDIN_CLIENT_SECRET")

        if not LINKEDIN_CLIENT_ID or not LINKEDIN_CLIENT_SECRET:
            print("⚠ OAuth disabled - LinkedIn credentials not configured")
            print("  Set LINKEDIN_CLIENT_ID and LINKEDIN_CLIENT_SECRET to enable OAuth")
            return None

        # Import LinkedIn-specific provider
        from .oauth.provider import LinkedInOAuthProvider

        # Get OAuth configuration from environment
        LINKEDIN_REDIRECT_URI = os.getenv(
            "LINKEDIN_REDIRECT_URI", "http://localhost:8000/oauth/callback"
        )

        # Validate credentials aren't test values
        if LINKEDIN_CLIENT_ID.startswith("test_") or LINKEDIN_CLIENT_SECRET.startswith("test_"):
            print("⚠️  WARNING: Using test LinkedIn credentials!")
            print("   OAuth flow will not work with test credentials.")
            print("   To use OAuth, obtain real credentials from:")
            print("   https://www.linkedin.com/developers/apps")

        # Create LinkedIn OAuth provider with SHARED token store
        oauth_provider = LinkedInOAuthProvider(
            linkedin_client_id=LINKEDIN_CLIENT_ID,
            linkedin_client_secret=LINKEDIN_CLIENT_SECRET,
            linkedin_redirect_uri=LINKEDIN_REDIRECT_URI,
            oauth_server_url=OAUTH_SERVER_URL,
            token_store=_global_token_store,
        )

        # Initialize generic OAuth middleware with LinkedIn provider
        oauth_middleware = OAuthMiddleware(
            mcp_server=mcp,
            provider=oauth_provider,
            oauth_server_url=OAUTH_SERVER_URL,
            callback_path="/oauth/callback",
            scopes_supported=[
                "linkedin.posts",
                "linkedin.profile",
                "linkedin.documents",
            ],
            service_documentation="https://github.com/chrishayuk/chuk-mcp-linkedin",
            provider_name="LinkedIn",
        )

        print("✓ OAuth enabled - LinkedIn direct mode")
        print(f"  OAuth server: {OAUTH_SERVER_URL}")
        print(f"  Discovery: {OAUTH_SERVER_URL}/.well-known/oauth-authorization-server")
        print(f"  Protected Resource: {OAUTH_SERVER_URL}/.well-known/oauth-protected-resource")

        return oauth_middleware

    else:
        print(f"⚠ Unknown OAUTH_MODE: {OAUTH_MODE}")
        print("  Valid modes: linkedin, keycloak")
        return None


def get_oauth_provider() -> Optional[Any]:
    """Get the global OAuth provider instance."""
    return oauth_provider


def get_token_store() -> Optional[Any]:
    """Get the global token store instance."""
    return _global_token_store


# Make tools available at module level for easier imports
__all__ = [
    "mcp",
    "manager",
    "linkedin_client",
    "draft_tools",
    "composition_tools",
    "theme_tools",
    "registry_tools",
    "publishing_tools",
]
