"""
Standardized FastAPI Service Base

Provides a unified approach for creating FastAPI microservices with common patterns:
- Logging setup
- Metrics initialization
- Health checks
- Database initialization
- CORS middleware
- Exception handlers
- Lifespan management
"""

import os
import structlog
from typing import Optional, List, Dict, Callable, Any, TYPE_CHECKING
from contextlib import asynccontextmanager
from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import JSONResponse
from fastapi.routing import APIRouter

from shared.monitoring import setup_logging, setup_otel_logging, setup_otel_metrics, setup_all_metrics
from shared.monitoring.metrics import setup_metrics_early
from shared.monitoring.health_checks import setup_fastapi_health_checks
from shared.monitoring.tracing import setup_tracing
from shared.database.base import DatabaseManager

if TYPE_CHECKING:
    import uvicorn


class BaseFastAPIService:
    """
    Base class for FastAPI microservices with standardized patterns
    """

    def __init__(
        self,
        service_name: str,
        app_name: str,
        description: str,
        version: str = "1.0.0",
        log_level: str = "INFO",
        cors_origins: Optional[List[str]] = None,
        api_prefix: str = "/api/v1",
        database_manager: Optional[DatabaseManager] = None,
        expected_tables: Optional[List[str]] = None,
        custom_health_checks: Optional[Dict[str, Callable[[], Any]]] = None,
        enable_metrics: bool = True,
        enable_health_checks: bool = True,
        enable_cors: bool = True,
        enable_exception_handlers: bool = True,
        enable_messaging: bool = False,
        enable_tracing: bool = True,
        custom_metrics: Optional[Dict[str, Dict[str, Any]]] = None,
        alert_service_class: Optional[type] = None
    ):
        self.service_name = service_name
        self.app_name = app_name
        self.description = description
        self.version = version
        self.log_level = log_level
        self.cors_origins = cors_origins or ["*"]
        self.api_prefix = api_prefix
        self.database_manager = database_manager
        self.expected_tables = expected_tables
        self.custom_health_checks = custom_health_checks or {}
        self.enable_metrics = enable_metrics
        self.enable_health_checks = enable_health_checks
        self.enable_cors = enable_cors
        self.enable_exception_handlers = enable_exception_handlers
        self.enable_messaging = enable_messaging
        self.enable_tracing = enable_tracing
        self.custom_metrics = custom_metrics or {}
        self.alert_service_class = alert_service_class

        # Initialize logging
        setup_logging(service_name, log_level)

        # Setup OpenTelemetry logging export if enabled
        if os.getenv("OTEL_LOGS_EXPORTER", "").lower() == "otlp":
            try:
                setup_otel_logging(service_name, version)
                self.logger = structlog.get_logger()
                self.logger.info(f"OpenTelemetry logs export enabled for {service_name}")
            except Exception as e:
                self.logger = structlog.get_logger()
                self.logger.warning(f"Failed to setup OpenTelemetry logs export: {e}")
        else:
            self.logger = structlog.get_logger()

        # Will be set during app creation
        self.app: Optional[FastAPI] = None
        self.metrics_collector = None
        self.health_manager = None
        self.alert_service = None

    def create_app(self, **fastapi_kwargs) -> FastAPI:
        """
        Create and configure FastAPI application with standardized setup
        """
        # Default FastAPI configuration
        default_config = {
            "title": self.app_name,
            "description": self.description,
            "version": self.version,
            "openapi_url": f"{self.api_prefix}/openapi.json",
            "docs_url": f"{self.api_prefix}/docs",
            "redoc_url": f"{self.api_prefix}/redoc",
        }

        # Merge with user-provided config
        config = {**default_config, **fastapi_kwargs}

        # Create FastAPI app
        self.app = FastAPI(**config)

        # Setup metrics BEFORE middleware and lifespan
        if self.enable_metrics:
            self.metrics_collector = setup_metrics_early(self.app, self.service_name)

            # Setup OpenTelemetry metrics export if enabled
            enable_otel_metrics = os.getenv("ENABLE_OTEL_METRICS", "true").lower() == "true"
            if enable_otel_metrics:
                try:
                    self.otel_meter_provider = setup_otel_metrics(self.service_name, self.version)
                    if self.otel_meter_provider:
                        self.logger.info(f"OpenTelemetry metrics export enabled for {self.service_name}")

                        # Setup system metrics collection (CPU, memory, disk, network)
                        enable_system_metrics = os.getenv("ENABLE_SYSTEM_METRICS", "true").lower() == "true"
                        if enable_system_metrics:
                            try:
                                self.system_metrics, self.app_metrics = setup_all_metrics(
                                    self.service_name,
                                    self.version,
                                    self.otel_meter_provider
                                )
                                self.logger.info(f"System metrics collection enabled for {self.service_name}")
                            except Exception as e:
                                self.logger.warning(f"Failed to setup system metrics: {e}")
                except Exception as e:
                    self.logger.warning(f"Failed to setup OpenTelemetry metrics export: {e}")

        # Setup distributed tracing
        # Check both constructor flag and environment variable
        tracing_enabled = self.enable_tracing and os.getenv("ENABLE_TRACING", "true").lower() == "true"

        if tracing_enabled:
            try:
                otel_endpoint = os.getenv(
                    "OTEL_COLLECTOR_ENDPOINT",
                    "http://signoz-otel-collector.bakery-ia:4318"
                )
                setup_tracing(self.app, self.service_name, self.version, otel_endpoint)
                self.logger.info(f"Distributed tracing enabled for {self.service_name}")
            except Exception as e:
                self.logger.warning(f"Failed to setup tracing, continuing without it: {e}")
        else:
            self.logger.info(f"Distributed tracing disabled for {self.service_name}")

        # Setup lifespan
        self.app.router.lifespan_context = self._create_lifespan()

        # Setup middleware
        if self.enable_cors:
            self._setup_cors()

        # Setup exception handlers
        if self.enable_exception_handlers:
            self._setup_exception_handlers()

        # Setup health checks
        if self.enable_health_checks:
            self._setup_health_checks()

        # Setup root endpoint
        self._setup_root_endpoint()

        return self.app

    def _create_lifespan(self):
        """Create lifespan context manager"""
        @asynccontextmanager
        async def lifespan(app: FastAPI):
            # Startup
            self.logger.info(f"Starting {self.service_name}", version=self.version)

            try:
                # Initialize database if provided
                if self.database_manager:
                    await self._initialize_database()

                # Setup messaging if enabled
                if self.enable_messaging:
                    await self._setup_messaging()

                # Initialize alert service if provided
                if self.alert_service_class:
                    await self._initialize_alert_service(app)

                # Register custom metrics if provided
                if self.custom_metrics:
                    self.register_custom_metrics(self.custom_metrics)

                # Custom startup logic
                await self.on_startup(app)

                # Mark service as ready
                app.state.ready = True

                self.logger.info(f"{self.service_name} started successfully")

                yield

            except Exception as e:
                self.logger.error(f"Startup failed for {self.service_name}", error=str(e))
                raise
            finally:
                # Shutdown
                self.logger.info(f"Shutting down {self.service_name}")
                try:
                    await self.on_shutdown(app)

                    # Cleanup alert service if it exists
                    if self.alert_service:
                        await self._cleanup_alert_service()

                    # Cleanup messaging if enabled
                    if self.enable_messaging:
                        await self._cleanup_messaging()

                    if self.database_manager:
                        await self._cleanup_database()

                except Exception as e:
                    self.logger.error(f"Shutdown error for {self.service_name}", error=str(e))

        return lifespan

    async def on_startup(self, app: FastAPI):
        """
        Override this method for custom startup logic
        Called after database initialization but before marking as ready
        """
        pass

    async def on_shutdown(self, app: FastAPI):
        """
        Override this method for custom shutdown logic
        Called before database cleanup
        """
        pass

    async def _initialize_database(self):
        """Initialize database connection and tables"""
        try:
            # Test connection
            if await self.database_manager.test_connection():
                self.logger.info("Database connection established")

                # Handle automatic table initialization
                await self._handle_database_tables()

                self.logger.info("Database initialized successfully")
            else:
                raise Exception("Database connection test failed")
        except Exception as e:
            self.logger.error("Database initialization failed", error=str(e))
            raise

    async def _handle_database_tables(self):
        """
        Verify database is ready for service startup.

        Services NEVER run migrations - they only verify the database
        has been properly initialized by the migration job.

        This ensures:
        - Fast service startup (50-80% faster)
        - No race conditions between replicas
        - Clear separation: migrations are operational, not application concern
        """
        try:
            # Import the init manager here to avoid circular imports
            from shared.database.init_manager import initialize_service_database

            # Services ALWAYS verify only (never run migrations)
            # Migrations are handled by dedicated migration jobs
            result = await initialize_service_database(
                database_manager=self.database_manager,
                service_name=self.service_name.replace("-service", "").replace("_", ""),
                verify_only=True  # Services only verify, never run migrations
            )

            self.logger.info("Database verification completed", result=result)

        except Exception as e:
            self.logger.error("Database verification failed", error=str(e))
            # FAIL FAST: If database not ready, service should not start
            raise

    async def _cleanup_database(self):
        """Cleanup database connections"""
        try:
            await self.database_manager.close_connections()
            self.logger.info("Database connections closed")
        except Exception as e:
            self.logger.error("Database cleanup error", error=str(e))

    async def _setup_messaging(self):
        """Setup messaging service - to be overridden by services that need it"""
        pass

    async def _cleanup_messaging(self):
        """Cleanup messaging service - to be overridden by services that need it"""
        pass

    async def _initialize_alert_service(self, app: FastAPI):
        """Initialize alert service - to be overridden by services that need it"""
        pass

    async def _cleanup_alert_service(self):
        """Cleanup alert service - to be overridden by services that need it"""
        pass

    def _setup_cors(self):
        """Setup CORS middleware"""
        if self.app is not None:
            self.app.add_middleware(
                CORSMiddleware,
                allow_origins=self.cors_origins,
                allow_credentials=True,
                allow_methods=["*"],
                allow_headers=["*"],
            )

    def _setup_exception_handlers(self):
        """Setup standard exception handlers"""
        if self.app is not None:
            @self.app.exception_handler(ValueError)
            async def value_error_handler(request: Request, exc: ValueError):
                """Handle validation errors"""
                return JSONResponse(
                    status_code=400,
                    content={
                        "error": "Validation Error",
                        "detail": str(exc),
                        "type": "value_error"
                    }
                )

            @self.app.exception_handler(Exception)
            async def general_exception_handler(request: Request, exc: Exception):
                """Handle general exceptions"""
                self.logger.error(
                    "Unhandled exception",
                    error=str(exc),
                    path=request.url.path,
                    method=request.method
                )

                # Record error metric if available
                if self.metrics_collector:
                    self.metrics_collector.increment_counter("errors_total", labels={"type": "unhandled"})

                return JSONResponse(
                    status_code=500,
                    content={
                        "error": "Internal Server Error",
                        "detail": "An unexpected error occurred",
                        "type": "internal_error"
                    }
                )

    def _setup_health_checks(self):
        """Setup health check endpoints"""
        self.health_manager = setup_fastapi_health_checks(
            app=self.app,
            service_name=self.service_name,
            version=self.version,
            database_manager=self.database_manager,
            expected_tables=self.expected_tables,
            custom_checks=self.custom_health_checks
        )

    def _setup_root_endpoint(self):
        """Setup root endpoint with service information"""
        if self.app is not None:
            @self.app.get("/")
            async def root():
                """Root endpoint with service information"""
                return {
                    "service": self.service_name,
                    "version": self.version,
                    "description": self.description,
                    "status": "running",
                    "docs_url": f"{self.api_prefix}/docs",
                    "health_url": "/health"
                }

    def add_router(self, router: APIRouter, **kwargs: Any):
        """Convenience method to add routers with default prefix"""
        if self.app is not None:
            prefix = kwargs.get('prefix', self.api_prefix)
            kwargs['prefix'] = prefix
            self.app.include_router(router, **kwargs)

    def register_custom_metrics(self, metrics_config: Dict[str, Dict[str, Any]]):
        """
        Register custom metrics for the service

        Args:
            metrics_config: Dict with metric name as key and config as value
                           Example: {
                               "user_registrations": {
                                   "type": "counter",
                                   "description": "Total user registrations",
                                   "labels": ["status"]
                               }
                           }
        """
        if not self.metrics_collector:
            self.logger.warning("Metrics collector not available")
            return

        for metric_name, config in metrics_config.items():
            metric_type = config.get("type", "counter")
            description = config.get("description", f"{metric_name} metric")
            labels = config.get("labels", [])

            if metric_type == "counter":
                self.metrics_collector.register_counter(metric_name, description, labels=labels)
            elif metric_type == "histogram":
                self.metrics_collector.register_histogram(metric_name, description, labels=labels)
            else:
                self.logger.warning(f"Unsupported metric type: {metric_type}")

    def run_development_server(self, host: str = "0.0.0.0", port: int = 8000, reload: Optional[bool] = None):
        """
        Run development server with uvicorn
        """
        import uvicorn

        if reload is None:
            reload = os.getenv("RELOAD", "false").lower() == "true"

        uvicorn.run(
            f"{self.__module__}:app",
            host=host,
            port=port,
            reload=reload,
            log_level="info"
        )


class StandardFastAPIService(BaseFastAPIService):
    """
    Standard service implementation for most microservices

    Provides additional common patterns for services with database and standard endpoints
    """

    def __init__(self, **kwargs: Any):
        super().__init__(**kwargs)

    def setup_standard_endpoints(self):
        """Setup standard service endpoints"""
        if self.app is not None:
            @self.app.get(f"{self.api_prefix}/info")
            async def service_info():
                """Service information endpoint"""
                return {
                    "service": self.service_name,
                    "version": self.version,
                    "description": self.description,
                    "api_version": "v1",
                    "environment": os.getenv("ENVIRONMENT", "development"),
                    "features": self.get_service_features()
                }

    def get_service_features(self) -> List[str]:
        """
        Override this method to return service-specific features
        """
        return ["health_checks", "metrics", "standardized_api"]


class MessageProcessorService(BaseFastAPIService):
    """
    Service implementation for message processing services

    Provides patterns for background processing services
    """

    def __init__(self, **kwargs: Any):
        # Message processors typically don't need CORS or full API setup
        kwargs.setdefault('enable_cors', False)
        kwargs.setdefault('api_prefix', '/api/v1')
        super().__init__(**kwargs)

    async def on_startup(self, app: FastAPI):
        """Initialize message processing components"""
        await super().on_startup(app)
        await self.setup_message_processing()

    async def on_shutdown(self, app: FastAPI):
        """Cleanup message processing components"""
        await self.cleanup_message_processing()
        await super().on_shutdown(app)

    async def setup_message_processing(self):
        """Override this method to setup message processing"""
        pass

    async def cleanup_message_processing(self):
        """Override this method to cleanup message processing"""
        pass