"""
FastAPI application for WiFi-DensePose API
"""

import asyncio
import logging
import logging.config
from contextlib import asynccontextmanager
from typing import Dict, Any

from fastapi import FastAPI, Request, Response
from fastapi.middleware.cors import CORSMiddleware
from fastapi.middleware.trustedhost import TrustedHostMiddleware
from fastapi.responses import JSONResponse
from fastapi.exceptions import RequestValidationError
from starlette.exceptions import HTTPException as StarletteHTTPException

from src.config.settings import get_settings
from src.config.domains import get_domain_config
from src.api.routers import pose, stream, health, auth
from src.api.middleware.auth import AuthMiddleware
from src.api.middleware.rate_limit import RateLimitMiddleware
from src.api.dependencies import get_pose_service, get_stream_service, get_hardware_service
from src.api.websocket.connection_manager import connection_manager
from src.api.websocket.pose_stream import PoseStreamHandler

# Configure logging
settings = get_settings()
logging.config.dictConfig(settings.get_logging_config())
logger = logging.getLogger(__name__)


@asynccontextmanager
async def lifespan(app: FastAPI):
    """Application lifespan manager."""
    logger.info("Starting WiFi-DensePose API...")
    
    try:
        # Initialize services
        await initialize_services(app)
        
        # Start background tasks
        await start_background_tasks(app)
        
        logger.info("WiFi-DensePose API started successfully")
        
        yield
        
    except Exception as e:
        logger.error(f"Failed to start application: {e}")
        raise
    finally:
        # Cleanup on shutdown
        logger.info("Shutting down WiFi-DensePose API...")
        await cleanup_services(app)
        logger.info("WiFi-DensePose API shutdown complete")


async def initialize_services(app: FastAPI):
    """Initialize application services."""
    try:
        # Initialize hardware service
        hardware_service = get_hardware_service()
        await hardware_service.initialize()
        
        # Initialize pose service
        pose_service = get_pose_service()
        await pose_service.initialize()
        
        # Initialize stream service
        stream_service = get_stream_service()
        await stream_service.initialize()
        
        # Initialize pose stream handler
        pose_stream_handler = PoseStreamHandler(
            connection_manager=connection_manager,
            pose_service=pose_service,
            stream_service=stream_service
        )
        
        # Store in app state for access in routes
        app.state.hardware_service = hardware_service
        app.state.pose_service = pose_service
        app.state.stream_service = stream_service
        app.state.pose_stream_handler = pose_stream_handler
        
        logger.info("Services initialized successfully")
        
    except Exception as e:
        logger.error(f"Failed to initialize services: {e}")
        raise


async def start_background_tasks(app: FastAPI):
    """Start background tasks."""
    try:
        # Start pose service
        pose_service = app.state.pose_service
        await pose_service.start()
        logger.info("Pose service started")
        
        # Start pose streaming if enabled
        if settings.enable_real_time_processing:
            pose_stream_handler = app.state.pose_stream_handler
            await pose_stream_handler.start_streaming()
        
        logger.info("Background tasks started")
        
    except Exception as e:
        logger.error(f"Failed to start background tasks: {e}")
        raise


async def cleanup_services(app: FastAPI):
    """Cleanup services on shutdown."""
    try:
        # Stop pose streaming
        if hasattr(app.state, 'pose_stream_handler'):
            await app.state.pose_stream_handler.shutdown()
        
        # Shutdown connection manager
        await connection_manager.shutdown()
        
        # Cleanup services
        if hasattr(app.state, 'stream_service'):
            await app.state.stream_service.shutdown()
        
        if hasattr(app.state, 'pose_service'):
            await app.state.pose_service.stop()
        
        if hasattr(app.state, 'hardware_service'):
            await app.state.hardware_service.shutdown()
        
        logger.info("Services cleaned up successfully")
        
    except Exception as e:
        logger.error(f"Error during cleanup: {e}")


# Create FastAPI application
app = FastAPI(
    title=settings.app_name,
    version=settings.version,
    description="WiFi-based human pose estimation and activity recognition API",
    docs_url=settings.docs_url if not settings.is_production else None,
    redoc_url=settings.redoc_url if not settings.is_production else None,
    openapi_url=settings.openapi_url if not settings.is_production else None,
    lifespan=lifespan
)

# Add middleware
if settings.enable_rate_limiting:
    app.add_middleware(RateLimitMiddleware)

if settings.enable_authentication:
    app.add_middleware(AuthMiddleware)

# Add CORS middleware
cors_config = settings.get_cors_config()
app.add_middleware(
    CORSMiddleware,
    **cors_config
)

# Add trusted host middleware for production
if settings.is_production:
    app.add_middleware(
        TrustedHostMiddleware,
        allowed_hosts=settings.allowed_hosts
    )


# Exception handlers
@app.exception_handler(StarletteHTTPException)
async def http_exception_handler(request: Request, exc: StarletteHTTPException):
    """Handle HTTP exceptions."""
    return JSONResponse(
        status_code=exc.status_code,
        content={
            "error": {
                "code": exc.status_code,
                "message": exc.detail,
                "type": "http_error"
            }
        }
    )


@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
    """Handle request validation errors."""
    return JSONResponse(
        status_code=422,
        content={
            "error": {
                "code": 422,
                "message": "Validation error",
                "type": "validation_error",
                "details": exc.errors()
            }
        }
    )


@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
    """Handle general exceptions."""
    logger.error(f"Unhandled exception: {exc}", exc_info=True)
    
    return JSONResponse(
        status_code=500,
        content={
            "error": {
                "code": 500,
                "message": "Internal server error",
                "type": "internal_error"
            }
        }
    )


# Middleware for request logging
@app.middleware("http")
async def log_requests(request: Request, call_next):
    """Log all requests."""
    start_time = asyncio.get_event_loop().time()
    
    # Process request
    response = await call_next(request)
    
    # Calculate processing time
    process_time = asyncio.get_event_loop().time() - start_time
    
    # Log request
    logger.info(
        f"{request.method} {request.url.path} - "
        f"Status: {response.status_code} - "
        f"Time: {process_time:.3f}s"
    )
    
    # Add processing time header
    response.headers["X-Process-Time"] = str(process_time)
    
    return response


# Include routers
app.include_router(
    health.router,
    prefix="/health",
    tags=["Health"]
)

app.include_router(
    pose.router,
    prefix=f"{settings.api_prefix}/pose",
    tags=["Pose Estimation"]
)

app.include_router(
    stream.router,
    prefix=f"{settings.api_prefix}/stream",
    tags=["Streaming"]
)

app.include_router(
    auth.router,
    prefix=f"{settings.api_prefix}",
    tags=["Authentication"]
)


# Root endpoint
@app.get("/")
async def root():
    """Root endpoint with API information."""
    return {
        "name": settings.app_name,
        "version": settings.version,
        "environment": settings.environment,
        "docs_url": settings.docs_url,
        "api_prefix": settings.api_prefix,
        "features": {
            "authentication": settings.enable_authentication,
            "rate_limiting": settings.enable_rate_limiting,
            "websockets": settings.enable_websockets,
            "real_time_processing": settings.enable_real_time_processing
        }
    }


# API information endpoint
@app.get(f"{settings.api_prefix}/info")
async def api_info():
    """Get detailed API information."""
    domain_config = get_domain_config()
    
    return {
        "api": {
            "name": settings.app_name,
            "version": settings.version,
            "environment": settings.environment,
            "prefix": settings.api_prefix
        },
        "configuration": {
            "zones": len(domain_config.zones),
            "routers": len(domain_config.routers),
            "pose_models": len(domain_config.pose_models)
        },
        "features": {
            "authentication": settings.enable_authentication,
            "rate_limiting": settings.enable_rate_limiting,
            "websockets": settings.enable_websockets,
            "real_time_processing": settings.enable_real_time_processing,
            "historical_data": settings.enable_historical_data
        },
        "limits": {
            "rate_limit_requests": settings.rate_limit_requests,
            "rate_limit_window": settings.rate_limit_window,
            "max_websocket_connections": domain_config.streaming.max_connections
        }
    }


# Status endpoint
@app.get(f"{settings.api_prefix}/status")
async def api_status(request: Request):
    """Get current API status."""
    try:
        # Get services from app state
        hardware_service = getattr(request.app.state, 'hardware_service', None)
        pose_service = getattr(request.app.state, 'pose_service', None)
        stream_service = getattr(request.app.state, 'stream_service', None)
        pose_stream_handler = getattr(request.app.state, 'pose_stream_handler', None)
        
        # Get service statuses
        status = {
            "api": {
                "status": "healthy",
                "uptime": "unknown",
                "version": settings.version
            },
            "services": {
                "hardware": await hardware_service.get_status() if hardware_service else {"status": "unavailable"},
                "pose": await pose_service.get_status() if pose_service else {"status": "unavailable"},
                "stream": await stream_service.get_status() if stream_service else {"status": "unavailable"}
            },
            "streaming": pose_stream_handler.get_stream_status() if pose_stream_handler else {"is_streaming": False},
            "connections": await connection_manager.get_connection_stats()
        }
        
        return status
        
    except Exception as e:
        logger.error(f"Error getting API status: {e}")
        return {
            "api": {
                "status": "error",
                "error": str(e)
            }
        }


# Metrics endpoint (if enabled)
if settings.metrics_enabled:
    @app.get(f"{settings.api_prefix}/metrics")
    async def api_metrics(request: Request):
        """Get API metrics."""
        try:
            # Get services from app state
            pose_stream_handler = getattr(request.app.state, 'pose_stream_handler', None)
            
            metrics = {
                "connections": await connection_manager.get_metrics(),
                "streaming": await pose_stream_handler.get_performance_metrics() if pose_stream_handler else {}
            }
            
            return metrics
            
        except Exception as e:
            logger.error(f"Error getting metrics: {e}")
            return {"error": str(e)}


# Development endpoints (only in development)
if settings.is_development and settings.enable_test_endpoints:
    @app.get(f"{settings.api_prefix}/dev/config")
    async def dev_config():
        """Get current configuration (development only).

        Returns a sanitized view -- secret keys and passwords are redacted.
        """
        _sensitive = {"secret", "password", "token", "key", "credential", "auth"}
        raw = settings.dict()
        sanitized = {
            k: "***REDACTED***" if any(s in k.lower() for s in _sensitive) else v
            for k, v in raw.items()
        }
        domain_config = get_domain_config()
        return {
            "settings": sanitized,
            "domain_config": domain_config.to_dict()
        }
    
    @app.post(f"{settings.api_prefix}/dev/reset")
    async def dev_reset(request: Request):
        """Reset services (development only)."""
        try:
            # Reset services
            hardware_service = getattr(request.app.state, 'hardware_service', None)
            pose_service = getattr(request.app.state, 'pose_service', None)
            
            if hardware_service:
                await hardware_service.reset()
            
            if pose_service:
                await pose_service.reset()
            
            return {"message": "Services reset successfully"}
            
        except Exception as e:
            logger.error(f"Error resetting services: {e}")
            return {"error": str(e)}


if __name__ == "__main__":
    import uvicorn
    
    uvicorn.run(
        "src.api.main:app",
        host=settings.host,
        port=settings.port,
        reload=settings.reload,
        workers=settings.workers if not settings.reload else 1,
        log_level=settings.log_level.lower()
    )