"""Structured JSON logging configuration."""

import json
import logging
import sys
import time
import traceback
from collections.abc import Callable

from fastapi import Request, Response
from starlette.middleware.base import BaseHTTPMiddleware

from src.services.shared.correlation import get_correlation_id

logger = logging.getLogger(__name__)


class CorrelationIdFilter(logging.Filter):
    """Inject correlation_id into every log record from context var."""

    def filter(self, record: logging.LogRecord) -> bool:
        record.correlation_id = get_correlation_id()  # type: ignore[attr-defined]
        return True


class JSONFormatter(logging.Formatter):
    """Emit log records as single-line JSON."""

    def format(self, record: logging.LogRecord) -> str:
        log_obj: dict = {
            "timestamp": self.formatTime(record),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
            "correlation_id": getattr(record, "correlation_id", None),
        }
        # Optional extra fields
        for key in ("instance_id", "event_type"):
            value = getattr(record, key, None)
            if value is not None:
                log_obj[key] = value
        if record.exc_info:
            log_obj["exception"] = self.formatException(record.exc_info)
        return json.dumps(log_obj, default=str)

    def formatTime(self, record: logging.LogRecord, datefmt: str | None = None) -> str:
        return time.strftime("%Y-%m-%dT%H:%M:%S", time.gmtime(record.created))


class RequestLoggingMiddleware(BaseHTTPMiddleware):
    """Log all HTTP requests with timing and status codes."""

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        start_time = time.time()
        client_host = request.client.host if request.client else "unknown"

        logger.info(
            "→ Request: %s %s (client: %s)",
            request.method,
            request.url.path,
            client_host,
        )

        try:
            response = await call_next(request)
            duration = time.time() - start_time

            logger.info(
                "← Response: %s %s → %d (%dms)",
                request.method,
                request.url.path,
                response.status_code,
                int(duration * 1000),
            )
            return response

        except Exception as exc:
            duration = time.time() - start_time
            logger.error(
                "✗ Error: %s %s → %s (%dms)\n%s",
                request.method,
                request.url.path,
                type(exc).__name__,
                int(duration * 1000),
                traceback.format_exc(),
            )
            raise


class ExceptionLoggingMiddleware(BaseHTTPMiddleware):
    """Catch and log all unhandled exceptions."""

    async def dispatch(self, request: Request, call_next: Callable) -> Response:
        try:
            return await call_next(request)
        except Exception:
            logger.critical(
                "Unhandled exception in %s %s:\n%s",
                request.method,
                request.url.path,
                traceback.format_exc(),
            )
            raise


def configure_logging(level: int = logging.INFO) -> None:
    """Configure structured JSON logging for the application."""
    formatter = JSONFormatter()

    # Console handler
    console_handler = logging.StreamHandler(sys.stdout)
    console_handler.setFormatter(formatter)
    console_handler.addFilter(CorrelationIdFilter())

    # Configure root logger
    root_logger = logging.getLogger()
    root_logger.setLevel(level)
    root_logger.handlers = [console_handler]

    # Set levels for specific loggers
    logging.getLogger("uvicorn").setLevel(logging.WARNING)
    logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
    logging.getLogger("sqlalchemy.engine").setLevel(logging.WARNING)

    logger.info("Logging configured at level %s", logging.getLevelName(level))
