from __future__ import annotations

import logging
from typing import Callable, Optional, Set

from starlette.middleware.base import BaseHTTPMiddleware
from starlette.requests import Request
from starlette.responses import JSONResponse, Response

from ..monitor import monitor as _monitor
from ..exceptions import LogguardError, LogguardNotInitializedError

log = logging.getLogger("loguard.fastapi")


class LoGuardMiddleware(BaseHTTPMiddleware):
    """
    FastAPI / Starlette middleware — auto-tracks HTTP errors and optionally
    enforces the blacklist on every request.

    Setup:
        from fastapi import FastAPI
        from logguard import monitor
        from logguard.integrations.fastapi import LogguardMiddleware

        app = FastAPI()
        monitor.init(api_key="lg_live_xxx", env="production")

        app.add_middleware(
            LogguardMiddleware,
            track_statuses={400, 401, 403, 404, 429, 500, 502, 503},
            enforce_blacklist=True,   # block blacklisted IPs before they hit your routes
        )

    With blacklist enforcement:
        Blocked IPs/users receive:  HTTP 403 {"detail": "Forbidden", "reason": "..."}
        Flagged IPs/users still pass through but the event is marked in meta.
    """

    def __init__(
        self,
        app,
        *,
        track_statuses: Optional[Set[int]] = None,
        enforce_blacklist: bool = False,
        get_user_id: Optional[Callable[[Request], Optional[str]]] = None,
    ):
        super().__init__(app)
        self._track_statuses = track_statuses or {400, 401, 403, 404, 429, 500, 502, 503}
        self._enforce_blacklist = enforce_blacklist
        self._get_user_id = get_user_id

    async def dispatch(self, request: Request, call_next) -> Response:
        ip = (
            (request.headers.get("x-forwarded-for") or "").split(",")[0].strip()
            or (request.client.host if request.client else "unknown")
        )

        # ── Blacklist check (before request hits your routes) ──────────────
        if self._enforce_blacklist:
            try:
                result = await _monitor.blacklist.acheck(type="ip", value=ip)
                if result.get("blacklisted") and result.get("action") == "block":
                    return JSONResponse(
                        status_code=403,
                        content={"detail": "Forbidden", "reason": result.get("reason", "blacklisted")},
                    )
            except LogguardNotInitializedError:
                log.warning("LoGuardMiddleware: monitor not initialized")
            except LogguardError as e:
                log.debug("LoGuardMiddleware blacklist check failed: %r", e)

        # ── Process request ────────────────────────────────────────────────
        response = await call_next(request)

        # ── Track errors ───────────────────────────────────────────────────
        if response.status_code in self._track_statuses:
            try:
                user_id = None
                if self._get_user_id:
                    try:
                        user_id = self._get_user_id(request)
                    except Exception:
                        pass

                event_type = "login_failed" if response.status_code == 401 else "http_error"

                await _monitor.aevent(
                    type=event_type,
                    ip=ip,
                    path=request.url.path,
                    status_code=response.status_code,
                    user_id=user_id,
                )
            except LogguardNotInitializedError:
                log.warning("LogguardMiddleware: monitor not initialized")
            except LogguardError as e:
                log.debug("LogguardMiddleware track failed: %r", e)

        return response
