"""
FastAPI application entry point for PyCharter API.

This module sets up the FastAPI application with all routes, middleware, and dependencies.
"""

from __future__ import annotations

from contextlib import asynccontextmanager
from typing import AsyncGenerator

from fastapi import Depends, FastAPI, Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.routing import APIRoute

from pycharter import __version__ as pycharter_version
from pycharter.api.dependencies.auth import get_current_user

# Import routers from v1
from pycharter.api.routes.v1 import (
    auth,
    contracts,
    docs,
    etl,
    evolution,
    metadata,
    quality,
    runs,
    schemas,
    settings,
    templates,
    tracking,
    validation,
    validation_jobs,
)

# Wiki routes (ontology, knowledge graph, governance)
from pycharter.wiki.api.routes.v1 import concept_graph as wiki_concept_graph
from pycharter.wiki.api.routes.v1 import discovery as wiki_discovery
from pycharter.wiki.api.routes.v1 import graph as wiki_graph
from pycharter.wiki.api.routes.v1 import ingestion as wiki_ingestion
from pycharter.wiki.api.routes.v1 import lineage as wiki_lineage
from pycharter.wiki.api.routes.v1 import ontology as wiki_ontology
from pycharter.wiki.api.routes.v1 import proposals as wiki_proposals
from pycharter.wiki.api.routes.v1 import rdf as wiki_rdf
from pycharter.wiki.api.routes.v1 import reviews as wiki_reviews
from pycharter.wiki.api.routes.v1 import schema as wiki_schema
from pycharter.wiki.api.routes.v1 import search as wiki_search
from pycharter.wiki.api.routes.v1 import versioning as wiki_versioning

# API version
API_VERSION = "v1"


@asynccontextmanager
async def lifespan(app: FastAPI) -> AsyncGenerator[None, None]:
    """
    Lifespan context manager for FastAPI application.

    Handles startup and shutdown events.
    """
    # Startup: Initialize resources if needed
    yield
    # Shutdown: Cleanup resources if needed


def create_application() -> FastAPI:
    """
    Create and configure FastAPI application.

    Returns:
        Configured FastAPI application instance
    """
    app = FastAPI(
        title="PyCharter API",
        description="REST API for PyCharter data contract management and validation",
        version=pycharter_version,
        docs_url="/docs",
        redoc_url="/redoc",
        openapi_url="/openapi.json",
        lifespan=lifespan,
    )

    # Add CORS middleware
    import os

    # Get CORS origins from environment variable
    cors_origins_env = os.getenv("CORS_ORIGINS", "").strip()
    cors_origins = (
        [origin.strip() for origin in cors_origins_env.split(",") if origin.strip()]
        if cors_origins_env
        else []
    )

    # Default behavior: In development (or when ENVIRONMENT not set), allow localhost origins
    # This makes development easier without requiring environment variables
    is_development = os.getenv("ENVIRONMENT") == "development"
    is_production = os.getenv("ENVIRONMENT") == "production"

    # If no explicit CORS origins set and not in production, allow localhost for development
    if not cors_origins and not is_production:
        cors_origins = [
            "http://localhost:3000",
            "http://localhost:3001",
            "http://localhost:3002",
            "http://127.0.0.1:3000",
            "http://127.0.0.1:3001",
            "http://127.0.0.1:3002",
        ]

    # Configure CORS middleware
    # Note: Can't use allow_credentials=True with allow_origins=["*"]
    # So we use specific origins for development, or "*" without credentials for production
    use_credentials = bool(cors_origins and "*" not in cors_origins)

    app.add_middleware(
        CORSMiddleware,
        allow_origins=(
            cors_origins if cors_origins else ["*"]
        ),  # Fallback to "*" if explicitly empty
        allow_credentials=use_credentials,
        allow_methods=["GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"],
        allow_headers=["*"],
    )

    # Auth router: no global auth dependency (login/logout public; /auth/me uses Depends in route)
    app.include_router(
        auth.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Auth"],
    )

    # Protected routers: require valid Bearer token (when auth enabled; when disabled, get_current_user returns dummy user)
    _auth_dep = [Depends(get_current_user)]
    app.include_router(
        contracts.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Contracts"],
        dependencies=_auth_dep,
    )
    app.include_router(
        metadata.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Metadata"],
        dependencies=_auth_dep,
    )
    app.include_router(
        schemas.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Schemas"],
        dependencies=_auth_dep,
    )
    app.include_router(
        validation.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Validation"],
        dependencies=_auth_dep,
    )
    app.include_router(
        quality.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Quality"],
        dependencies=_auth_dep,
    )
    app.include_router(
        templates.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Templates"],
        dependencies=_auth_dep,
    )
    app.include_router(
        etl.router,
        prefix=f"/api/{API_VERSION}",
        tags=["ETL"],
        dependencies=_auth_dep,
    )
    app.include_router(
        runs.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Pipeline Runs"],
        dependencies=_auth_dep,
    )
    app.include_router(
        settings.public_router,
        prefix=f"/api/{API_VERSION}",
        tags=["Settings"],
    )
    app.include_router(
        settings.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Settings"],
        dependencies=_auth_dep,
    )
    # Docs (playground-routes): no auth so UI can load route list without token
    app.include_router(
        docs.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Documentation"],
    )
    app.include_router(
        tracking.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Quality Tracking"],
        dependencies=_auth_dep,
    )
    app.include_router(
        evolution.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Schema Evolution"],
        dependencies=_auth_dep,
    )

    app.include_router(
        validation_jobs.router,
        prefix=f"/api/{API_VERSION}",
        tags=["Validation Jobs"],
        dependencies=_auth_dep,
    )

    # Wiki routes (under /wiki/ prefix to avoid collisions with existing routes)
    app.include_router(
        wiki_ontology.router,
        prefix=f"/api/{API_VERSION}/wiki/ontology",
        tags=["Wiki - Ontology"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_versioning.router,
        prefix=f"/api/{API_VERSION}/wiki/versioning",
        tags=["Wiki - Versioning"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_proposals.router,
        prefix=f"/api/{API_VERSION}/wiki/proposals",
        tags=["Wiki - Proposals"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_reviews.router,
        prefix=f"/api/{API_VERSION}/wiki/reviews",
        tags=["Wiki - Reviews"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_graph.router,
        prefix=f"/api/{API_VERSION}/wiki/graph",
        tags=["Wiki - Graph"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_search.router,
        prefix=f"/api/{API_VERSION}/wiki/search",
        tags=["Wiki - Search"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_ingestion.router,
        prefix=f"/api/{API_VERSION}/wiki/ingestion",
        tags=["Wiki - Ingestion"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_lineage.router,
        prefix=f"/api/{API_VERSION}/wiki/lineage",
        tags=["Wiki - Lineage"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_discovery.router,
        prefix=f"/api/{API_VERSION}/wiki/discovery",
        tags=["Wiki - Discovery"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_rdf.router,
        prefix=f"/api/{API_VERSION}/wiki/rdf",
        tags=["Wiki - RDF / JSON-LD / SHACL"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_concept_graph.router,
        prefix=f"/api/{API_VERSION}/wiki/concept-graph",
        tags=["Wiki - Concept Graph"],
        dependencies=_auth_dep,
    )
    app.include_router(
        wiki_schema.router,
        prefix=f"/api/{API_VERSION}/wiki/schema",
        tags=["Wiki - Schema"],
        dependencies=_auth_dep,
    )

    # Root endpoint
    @app.get(
        "/",
        summary="API Information",
        description="Get API information and version",
        tags=["General"],
    )
    async def root() -> dict:
        """Root endpoint with API information."""
        return {
            "name": "PyCharter API",
            "version": pycharter_version,
            "api_version": API_VERSION,
            "docs": "/docs",
            "redoc": "/redoc",
        }

    # Health check endpoint
    @app.get(
        "/health",
        summary="Health Check",
        description="Check API health status",
        tags=["General"],
    )
    async def health_check() -> dict:
        """Health check endpoint."""
        return {"status": "healthy", "version": pycharter_version}

    # Request validation error handler
    @app.exception_handler(RequestValidationError)
    async def validation_exception_handler(
        request: Request, exc: RequestValidationError
    ) -> JSONResponse:
        """Handle request validation errors."""

        # Convert errors to JSON-serializable format
        def serialize_error(error: dict) -> dict:
            """Recursively serialize error dict, converting exceptions to strings."""
            serialized = {}
            for key, value in error.items():
                if isinstance(value, Exception):
                    serialized[key] = str(value)
                elif isinstance(value, dict):
                    serialized[key] = serialize_error(value)
                elif isinstance(value, (list, tuple)):
                    serialized[key] = [
                        (
                            serialize_error(item)
                            if isinstance(item, dict)
                            else str(item) if isinstance(item, Exception) else item
                        )
                        for item in value
                    ]
                else:
                    serialized[key] = value
            return serialized

        errors = [serialize_error(err) for err in exc.errors()]

        return JSONResponse(
            status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
            content={
                "error": "Validation error",
                "details": errors,
            },
        )

    # Global exception handler
    @app.exception_handler(Exception)
    async def global_exception_handler(
        request: Request, exc: Exception
    ) -> JSONResponse:
        """Global exception handler for unhandled errors."""
        import logging
        import os

        logger = logging.getLogger(__name__)
        logger.error("Unhandled exception: %s", exc, exc_info=True)

        # In production, don't expose internal error details
        is_development = os.getenv("ENVIRONMENT") == "development"

        error_detail = {
            "error": "Internal server error",
        }

        if is_development:
            error_detail.update(
                {
                    "message": str(exc),
                    "type": type(exc).__name__,
                }
            )

        return JSONResponse(
            status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
            content=error_detail,
        )

    # Override openapi() method to handle schema generation errors gracefully
    # This is a workaround for a Pydantic v2 issue with unhashable types in schema generation
    original_openapi = app.openapi

    def openapi_with_error_handling():
        """OpenAPI schema with error handling for unhashable types."""
        try:
            return original_openapi()
        except TypeError as e:
            if "unhashable type" in str(e):
                import logging

                logger = logging.getLogger(__name__)
                logger.warning(
                    "OpenAPI schema generation encountered an issue: %s. "
                    "Returning minimal schema.",
                    e,
                )
                # Return a minimal valid OpenAPI schema as fallback
                return {
                    "openapi": "3.1.0",
                    "info": {
                        "title": app.title,
                        "version": app.version,
                        "description": app.description,
                    },
                    "paths": {},
                    "components": {"schemas": {}},
                }
            raise

    app.openapi = openapi_with_error_handling

    return app


# Create application instance
app = create_application()


def main():
    """Main entry point for running the API server."""
    import uvicorn

    uvicorn.run(
        "pycharter.api.main:app",
        host="0.0.0.0",
        port=8002,
        reload=True,  # Set to False in production
    )


if __name__ == "__main__":
    main()
