"""Middlewares for IP blocking and visitor analytics."""

from __future__ import annotations

import logging
from datetime import timedelta

from django.conf import settings
from django.core.cache import cache
from django.http import HttpResponseForbidden, JsonResponse
from django.utils.deprecation import MiddlewareMixin
from django.utils import timezone

from cms.visitor_intel import get_client_ip, is_private_ip, is_valid_ip
from cms.visitor_services import ANALYTICS, BLOCKLIST

logger = logging.getLogger(__name__)

_BLOCK_PAGE = """<!doctype html>
<html lang="en"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="robots" content="noindex,nofollow">
<title>Access denied</title>
<style>
 body{{font-family:-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif;
      background:#0f172a;color:#e2e8f0;display:flex;min-height:100vh;margin:0;
      align-items:center;justify-content:center;text-align:center}}
 .box{{max-width:520px;padding:40px}}
 h1{{font-size:26px;margin:0 0 12px}}
 p{{color:#94a3b8;line-height:1.6}}
 code{{background:#1e293b;padding:2px 6px;border-radius:4px;color:#f87171}}
</style></head>
<body><div class="box">
<h1>Access denied</h1>
<p>Your IP address <code>{ip}</code> has been blocked from accessing this website
because of activity that violated our usage policy.</p>
<p>If you believe this is a mistake, contact <a href="mailto:{email}" style="color:#38bdf8">{email}</a>.</p>
</div></body></html>"""


class IPBlockMiddleware(MiddlewareMixin):
    """Reject requests coming from blocked IP addresses.

    Place this after ``AuthenticationMiddleware`` so staff can be exempted, but
    before view processing so blocked traffic never reaches the application.
    """

    def process_request(self, request):
        if not getattr(settings, 'IP_BLOCKING_ENABLED', True):
            return None

        # Staff/superusers are never locked out of their own site.
        if getattr(settings, 'IP_BLOCK_EXEMPT_STAFF', True):
            user = getattr(request, 'user', None)
            if user is not None and user.is_authenticated and (user.is_staff or user.is_superuser):
                return None

        ip_address = get_client_ip(request)

        # Flood protection: a burst of page visits from one IP is the classic
        # scraping/flooding signature. Auto-block offenders for a few hours.
        if self._flood_blocked(ip_address, request):
            return self._deny(ip_address, request)

        match = BLOCKLIST.is_blocked(ip_address)
        if not match:
            return None

        BLOCKLIST.record_hit(match.id, ip_address)
        logger.info("Blocked request from %s (%s) to %s", ip_address, match.reason or match.source, request.path)
        return self._deny(ip_address, request)

    def _flood_blocked(self, ip_address, request):
        if not getattr(settings, 'IP_FLOOD_PROTECTION', True):
            return False
        if not is_valid_ip(ip_address) or is_private_ip(ip_address):
            return False
        if request.path.startswith(('/admin/', '/static/', '/media/')):
            return False

        window = getattr(settings, 'IP_FLOOD_WINDOW_SECONDS', 60)
        threshold = getattr(settings, 'IP_FLOOD_THRESHOLD_REQUESTS', 120)
        cache_key = f'ipflood:{ip_address}'

        try:
            count = cache.get(cache_key, 0) + 1
            cache.set(cache_key, count, window)
        except Exception:
            return False

        if count <= threshold:
            return False

        if is_private_ip(ip_address) or ip_address in getattr(settings, 'IP_BLOCK_WHITELIST', []):
            return False

        BLOCKLIST.add_block(
            ip_address,
            reason=f'Flood guard: {count} requests in {window}s',
            source='auto',
            block_type='single',
            expires_at=timezone.now() + timedelta(hours=getattr(settings, 'IP_FLOOD_BLOCK_HOURS', 6)),
        )
        logger.warning("Flood guard auto-blocked %s after %s requests in %ss", ip_address, count, window)
        return True

    def _deny(self, ip_address, request):
        if request.path.startswith('/api/') or 'application/json' in request.META.get('HTTP_ACCEPT', ''):
            return JsonResponse(
                {'detail': 'Access denied. Your IP has been blocked.'},
                status=403,
            )

        email = getattr(settings, 'CONTACT_EMAIL_FOR_BLOCKS', 'info@xinfinitygroup.com')
        return HttpResponseForbidden(
            _BLOCK_PAGE.format(ip=ip_address, email=email)
        )


class VisitorTrackingMiddleware(MiddlewareMixin):
    """Record visitor sessions and page views after a successful response."""

    def __init__(self, get_response):
        super().__init__(get_response)
        # Imported lazily so settings are guaranteed to be configured.
        from cms.visitor_services import VisitorTrackingService

        self.tracker = VisitorTrackingService()

    def process_response(self, request, response):
        try:
            status = getattr(response, 'status_code', 200)
            content_type = response.get('Content-Type', '') if hasattr(response, 'get') else ''
            if status < 400 and ('text/html' in content_type or content_type == ''):
                self.tracker.track(request, response)
        except Exception:
            logger.debug("VisitorTrackingMiddleware error", exc_info=True)
        return response
