from __future__ import annotations

import json
import logging
import threading
from typing import Callable, Optional, Set

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

log = logging.getLogger("logguard.django")


class LogguardMiddleware:
    """
    Django middleware — auto-tracks HTTP errors and optionally enforces
    the blacklist on every incoming request.

    Setup in settings.py:
        MIDDLEWARE = [
            "logguard.integrations.django.LogguardMiddleware",
            ...  # put first so blacklist check happens early
        ]

    And in your app startup (e.g. AppConfig.ready()):
        from logguard import monitor
        monitor.init(api_key="lg_live_xxx", env="production")

    With blacklist enforcement:
        LOGGUARD_ENFORCE_BLACKLIST = True   # in settings.py
        or pass enforce_blacklist=True to the middleware constructor (via MIDDLEWARE_CONFIG)

    Blocked IPs receive: HTTP 403 JSON response.
    """

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

    def __call__(self, request):
        from django.http import JsonResponse

        ip = (
            request.META.get("HTTP_X_FORWARDED_FOR", "").split(",")[0].strip()
            or request.META.get("REMOTE_ADDR", "unknown")
        )

        # ── Blacklist check ────────────────────────────────────────────────
        if self._enforce_blacklist:
            try:
                result = _monitor.blacklist.check(type="ip", value=ip)
                if result.get("blacklisted") and result.get("action") == "block":
                    return JsonResponse(
                        {"detail": "Forbidden", "reason": result.get("reason", "blacklisted")},
                        status=403,
                    )
            except LogguardNotInitializedError:
                log.warning("LogguardMiddleware: monitor not initialized")
            except LogguardError as e:
                log.debug("LogguardMiddleware blacklist check failed: %r", e)

        # ── Process request ────────────────────────────────────────────────
        response = self.get_response(request)

        # ── Track errors ───────────────────────────────────────────────────
        if response.status_code in self._track_statuses:
            user_id = None
            if getattr(request, "user", None) and request.user.is_authenticated:
                user_id = str(request.user.pk)

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

            threading.Thread(
                target=self._fire,
                args=(event_type, ip, request.path, response.status_code, user_id),
                daemon=True,
            ).start()

        return response

    def _fire(self, event_type, ip, path, status_code, user_id):
        try:
            _monitor.event(
                type=event_type,
                ip=ip,
                path=path,
                status_code=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)
