"""
Author: Vasiliy Zdanovskiy
email: vasilyvz@gmail.com

Application factory for MCP Proxy Adapter API.
"""

import json
from pathlib import Path
from typing import Any, Dict, Optional

from fastapi import FastAPI, Body
from typing import List, Union
from mcp_proxy_adapter.api.handlers import (
    execute_command,
    handle_json_rpc,
    handle_batch_json_rpc,
    get_server_health,
    get_commands_list,
)

# from mcp_proxy_adapter.api.middleware import setup_middleware
try:
    from mcp_proxy_adapter.api.schemas import (
        JsonRpcRequest,
        JsonRpcSuccessResponse,
        JsonRpcErrorResponse,
        HealthResponse,
        CommandListResponse,
        APIToolDescription,
    )
except Exception:
    # If schemas are unavailable, define minimal type aliases to satisfy annotations
    JsonRpcRequest = Dict[str, Any]  # type: ignore
    JsonRpcSuccessResponse = Dict[str, Any]  # type: ignore
    JsonRpcErrorResponse = Dict[str, Any]  # type: ignore
    HealthResponse = Dict[str, Any]  # type: ignore
    CommandListResponse = Dict[str, Any]  # type: ignore
    APIToolDescription = Dict[str, Any]  # type: ignore

try:
    from mcp_proxy_adapter.api.tools import get_tool_description, execute_tool
except Exception:
    get_tool_description = None
    execute_tool = None
from mcp_proxy_adapter.core.logging import get_global_logger
from mcp_proxy_adapter.commands.command_registry import registry
from mcp_proxy_adapter.custom_openapi import custom_openapi_with_fallback
from .ssl_context_factory import SSLContextFactory
from .lifespan_manager import LifespanManager


class AppFactory:
    """Factory for creating FastAPI applications."""

    def __init__(self):
        """Initialize app factory."""
        self.logger = get_global_logger()
        self.ssl_factory = SSLContextFactory()
        self.lifespan_manager = LifespanManager()

    def create_app(
        self,
        title: Optional[str] = None,
        description: Optional[str] = None,
        version: Optional[str] = None,
        app_config: Optional[Dict[str, Any]] = None,
        config_path: Optional[str] = None,
    ) -> FastAPI:
        """
        Creates and configures FastAPI application.

        Args:
            title: Application title (default: "MCP Proxy Adapter")
            description: Application description (default: "JSON-RPC API for interacting with MCP Proxy")
            version: Application version (default: "1.0.0")
            app_config: Application configuration dictionary (optional)
            config_path: Path to configuration file (optional)

        Returns:
            Configured FastAPI application.

        Raises:
            SystemExit: If authentication is enabled but required files are missing (security issue)
        """
        # Use provided configuration or fallback to global config
        if app_config is not None:
            if hasattr(app_config, "get_all"):
                current_config = app_config.get_all()
            elif hasattr(app_config, "keys"):
                current_config = app_config
            else:
                # If app_config is not a dict-like object, use it as is
                current_config = app_config
        else:
            # If no app_config provided, try to get global config
            try:
                from mcp_proxy_adapter.config import get_config
                current_config = get_config().get_all()
            except Exception:
                # If global config is not available, create empty config
                current_config = {}

        # Debug: Check what config is passed to create_app
        if app_config:
            if hasattr(app_config, "keys"):
                print(
                    f"🔍 Debug: create_app received app_config keys: {list(app_config.keys())}"
                )
                # Debug SSL configuration
                protocol = app_config.get("server", {}).get("protocol", "http")
                verify_client = app_config.get("transport", {}).get("verify_client", False)
                ssl_enabled = protocol in ["https", "mtls"] or verify_client
                print(f"🔍 Debug: create_app SSL config: enabled={ssl_enabled}")
                print(f"🔍 Debug: create_app protocol: {protocol}")
                print(f"🔍 Debug: create_app verify_client: {verify_client}")
            else:
                print(f"🔍 Debug: create_app received app_config type: {type(app_config)}")
        else:
            print("🔍 Debug: create_app received no app_config, using global config")

        # Security check: Validate configuration strictly at startup (fail-fast)
        self._validate_configuration(current_config)

        # Security check: Validate all authentication configurations before startup
        self._validate_security_configuration(current_config)

        # Set default values
        title = title or "MCP Proxy Adapter"
        description = description or "JSON-RPC API for interacting with MCP Proxy"
        version = version or "1.0.0"

        # Create lifespan manager
        lifespan = self.lifespan_manager.create_lifespan(config_path, current_config)

        # Create FastAPI application
        app = FastAPI(
            title=title,
            description=description,
            version=version,
            lifespan=lifespan,
        )

        # Setup middleware - disabled for now
        # setup_middleware(app, current_config)

        # Setup routes
        self._setup_routes(app)

        # Setup OpenAPI
        app.openapi = lambda: custom_openapi_with_fallback(app)

        return app

    def _validate_configuration(self, current_config: Dict[str, Any]) -> None:
        """Validate configuration at startup."""
        try:
            from mcp_proxy_adapter.core.config_validator import ConfigValidator

            validator = ConfigValidator()
            validator.config_data = current_config
            validation_results = validator.validate_config()
            errors = [r for r in validation_results if r.level == "error"]
            warnings = [r for r in validation_results if r.level == "warning"]
            
            if errors:
                self.logger.critical("CRITICAL CONFIG ERROR: Invalid configuration at startup:")
                for error in errors:
                    self.logger.critical(f"  - {error.message}")
                raise SystemExit(1)
            for warning in warnings:
                self.logger.warning(f"Config warning: {warning.message}")
        except Exception as ex:
            self.logger.error(f"Failed to run startup configuration validation: {ex}")

    def _validate_security_configuration(self, current_config: Dict[str, Any]) -> None:
        """Validate security configuration at startup."""
        security_errors = []

        print(f"🔍 Debug: current_config keys: {list(current_config.keys())}")
        if "security" in current_config:
            print(f"🔍 Debug: security config: {current_config['security']}")
        if "roles" in current_config:
            print(f"🔍 Debug: roles config: {current_config['roles']}")

        # Check security framework configuration only if enabled
        security_config = current_config.get("security", {})
        if security_config.get("enabled", False):
            # Validate security framework configuration
            from mcp_proxy_adapter.core.unified_config_adapter import UnifiedConfigAdapter

            adapter = UnifiedConfigAdapter()
            validation_result = adapter.validate_configuration(current_config)

            if not validation_result.is_valid:
                security_errors.extend(validation_result.errors)

        # Check roles configuration only if enabled
        roles_config = current_config.get("roles", {})
        if roles_config.get("enabled", False):
            # Validate roles configuration
            from mcp_proxy_adapter.core.role_utils import RoleUtils

            role_utils = RoleUtils()
            validation_result = role_utils.validate_configuration(current_config)

            if not validation_result.is_valid:
                security_errors.extend(validation_result.errors)

        # Fail if there are security errors
        if security_errors:
            self.logger.critical("CRITICAL SECURITY ERROR: Invalid security configuration at startup:")
            for error in security_errors:
                self.logger.critical(f"  - {error}")
            raise SystemExit(1)

    def _setup_routes(self, app: FastAPI) -> None:
        """Setup application routes."""
        @app.get("/health", response_model=HealthResponse)
        async def health():  # type: ignore
            return await get_server_health()  # type: ignore[misc]

        @app.get("/commands", response_model=CommandListResponse)
        async def commands():  # type: ignore
            return await get_commands_list()  # type: ignore[misc]

        @app.post("/api/jsonrpc", response_model=Union[JsonRpcSuccessResponse, JsonRpcErrorResponse])
        async def jsonrpc(request: JsonRpcRequest):  # type: ignore
            return await handle_json_rpc(request.dict())  # type: ignore[misc]

        @app.post("/api/jsonrpc/batch", response_model=List[Union[JsonRpcSuccessResponse, JsonRpcErrorResponse]])
        async def jsonrpc_batch(requests: List[JsonRpcRequest]):  # type: ignore
            return await handle_batch_json_rpc([req.dict() for req in requests])  # type: ignore[misc]

        # Optional tool endpoints if tools module is available
        if get_tool_description and execute_tool:
            @app.get("/api/tools", response_model=List[APIToolDescription])
            async def tools():  # type: ignore
                return await get_tool_description()  # type: ignore[misc]

            @app.post("/api/tools/{tool_name}")
            async def execute_tool_endpoint(tool_name: str, params: Dict[str, Any] = Body(...)):
                return await execute_tool(tool_name, params)  # type: ignore[misc]
