import hashlib
import hmac
import logging
import time

from django.conf import settings
from django.utils import timezone
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView

from licenses.models import Installation, License, LicenseValidation
from licenses.serializers import LicenseValidateRequestSerializer

logger = logging.getLogger("licenses")

RATE_LIMIT_WINDOW = 60
MAX_REQUESTS_PER_WINDOW = 30


class _RateLimitStore:
    _attempts: dict[str, list[float]] = {}

    def is_rate_limited(self, key: str) -> bool:
        now = time.time()
        window_start = now - RATE_LIMIT_WINDOW
        attempts = self._attempts.get(key, [])
        attempts = [t for t in attempts if t > window_start]
        self._attempts[key] = attempts
        return len(attempts) >= MAX_REQUESTS_PER_WINDOW

    def record(self, key: str):
        self._attempts.setdefault(key, []).append(time.time())


_rate_limit = _RateLimitStore()


class LicenseValidateView(APIView):
    authentication_classes = []
    permission_classes = []

    def post(self, request):
        serializer = LicenseValidateRequestSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        data = serializer.validated_data

        license_key = data["license_key"]
        installation_id = data["installation_id"]
        domain = data["domain"]

        ip = self._get_client_ip(request)
        user_agent = request.META.get("HTTP_USER_AGENT", "")

        rate_key = f"{ip}:{license_key}"
        if _rate_limit.is_rate_limited(rate_key):
            logger.warning("Rate limit exceeded for %s", ip)
            return Response(
                {"valid": False, "reason": "rate_limited"},
                status=status.HTTP_429_TOO_MANY_REQUESTS,
            )
        _rate_limit.record(rate_key)

        license_obj = License.objects.filter(license_key=license_key).first()
        if not license_obj:
            self._log(
                None, LicenseValidation.RejectionReason.LICENSE_NOT_FOUND,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(LicenseValidation.RejectionReason.LICENSE_NOT_FOUND)

        installation = Installation.objects.filter(installation_id=installation_id).first()
        if not installation:
            self._log(
                None, LicenseValidation.RejectionReason.INSTALLATION_NOT_FOUND,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(LicenseValidation.RejectionReason.INSTALLATION_NOT_FOUND)

        if installation.license_id != license_obj.id:
            self._log(
                installation, LicenseValidation.RejectionReason.INSTALLATION_NOT_FOUND,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(LicenseValidation.RejectionReason.INSTALLATION_NOT_FOUND)

        if installation.domain.lower() != domain.lower():
            self._log(
                installation, LicenseValidation.RejectionReason.DOMAIN_MISMATCH,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(LicenseValidation.RejectionReason.DOMAIN_MISMATCH)

        if license_obj.status != License.Status.ACTIVE:
            reason = (
                LicenseValidation.RejectionReason.LICENSE_INACTIVE
                if license_obj.status == License.Status.INACTIVE
                else LicenseValidation.RejectionReason.LICENSE_INACTIVE
            )
            self._log(
                installation, reason,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(reason)

        now = timezone.now()
        if license_obj.expires_at and license_obj.expires_at < now:
            self._log(
                installation, LicenseValidation.RejectionReason.LICENSE_EXPIRED,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(LicenseValidation.RejectionReason.LICENSE_EXPIRED)

        if installation.status != Installation.Status.ACTIVE:
            self._log(
                installation, LicenseValidation.RejectionReason.INSTALLATION_INACTIVE,
                ip, user_agent, license_key, installation_id, domain,
            )
            return self._reject(LicenseValidation.RejectionReason.INSTALLATION_INACTIVE)

        installation.last_validated_at = now
        installation.ip_address = ip
        installation.save(update_fields=["last_validated_at", "ip_address"])

        self._log(
            installation, None,
            ip, user_agent, license_key, installation_id, domain,
            result=LicenseValidation.Result.SUCCESS,
        )

        return Response({
            "valid": True,
            "license_type": license_obj.license_type,
            "demo_mode": license_obj.demo_mode,
            "status": license_obj.status,
            "expires_at": license_obj.expires_at.isoformat() if license_obj.expires_at else None,
            "max_users": license_obj.max_users,
            "capabilities": license_obj.get_capabilities(),
        })

    def _get_client_ip(self, request):
        x_forwarded = request.META.get("HTTP_X_FORWARDED_FOR")
        if x_forwarded:
            return x_forwarded.split(",")[0].strip()
        return request.META.get("REMOTE_ADDR")

    def _reject(self, reason):
        return Response(
            {"valid": False, "reason": reason},
            status=status.HTTP_200_OK,
        )

    def _log(
        self,
        installation,
        reason,
        ip,
        user_agent,
        license_key,
        installation_id,
        domain,
        result=None,
    ):
        if result is None:
            result = LicenseValidation.Result.FAILURE
        LicenseValidation.objects.create(
            installation=installation,
            result=result,
            rejection_reason=reason or "",
            ip_address=ip,
            probalis_version=installation.probalis_version if installation else "",
            license_key_used=license_key,
            installation_id_used=installation_id,
            domain_used=domain,
            user_agent=user_agent[:500] if user_agent else "",
        )
        log_msg = f"Validation: {result} - {reason or 'ok'} - {license_key} - {ip}"
        if result == LicenseValidation.Result.FAILURE:
            logger.warning(log_msg)
        else:
            logger.info(log_msg)
