"""
Stripe webhook HTTP receiver view for django_stripe_billing.

Security checks:
1. Stripe signature verification (STRIPE_BILLING['STRIPE_WEBHOOK_SECRET'])
2. livemode / environment consistency guard
3. Duplicate event detection via DB unique constraint on stripe_event_id

Always returns 200 after persisting (or detecting a duplicate) so Stripe does
not retry.  All processing errors are handled by the Celery task with its own
retry / backoff policy.
"""

from __future__ import annotations

import logging

import stripe
from django.db import IntegrityError, transaction
from django.http import HttpRequest, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST

from .conf import require_stripe_secret_key, require_webhook_secret, stripe_billing_settings
from .models import StripeWebhookEvent

logger = logging.getLogger(__name__)


@csrf_exempt
@require_POST
def stripe_webhook(request: HttpRequest) -> HttpResponse:
    """
    Stripe webhook receiver.

    Mount this view in your project's ``urls.py``::

        from django.urls import path, include
        urlpatterns = [
            path('webhooks/', include('django_stripe_billing.urls')),
        ]

    Or use the URL directly::

        from django_stripe_billing.views import stripe_webhook
        path('webhooks/stripe/', stripe_webhook, name='stripe-webhook'),
    """
    stripe.api_key = require_stripe_secret_key()
    payload = request.body

    # ------------------------------------------------------------------
    # 1. Signature verification
    # ------------------------------------------------------------------
    signature_header = request.META.get("HTTP_STRIPE_SIGNATURE")
    if not signature_header:
        logger.warning("[WEBHOOK] No Stripe-Signature header present")
        return HttpResponse(status=400)

    try:
        event = stripe.Webhook.construct_event(
            payload,
            signature_header,
            require_webhook_secret(),
        )
    except ValueError:
        logger.warning("[WEBHOOK] Invalid payload")
        return HttpResponse(status=400)
    except stripe.error.SignatureVerificationError:
        logger.warning("[WEBHOOK] Invalid signature")
        return HttpResponse(status=400)

    event_id = event.get("id", "")
    event_type = event.get("type", "")
    livemode = event.get("livemode", True)
    api_version = event.get("api_version", "")

    # ------------------------------------------------------------------
    # 2. livemode / environment guard
    # ------------------------------------------------------------------
    is_production = stripe_billing_settings.ENVIRONMENT == "production"

    if livemode and not is_production:
        logger.warning(
            "[WEBHOOK] Received LIVE event %s in non-production environment (%s) — skipping.",
            event_id,
            stripe_billing_settings.ENVIRONMENT,
        )
        return HttpResponse(status=200)

    if not livemode and is_production:
        logger.warning(
            "[WEBHOOK] Received TEST event %s in production environment — skipping.",
            event_id,
        )
        return HttpResponse(status=200)

    # ------------------------------------------------------------------
    # 3. Persist for idempotency
    # ------------------------------------------------------------------
    payload_str = payload.decode("utf-8", errors="replace")[:10_000]

    try:
        with transaction.atomic():
            webhook_event = StripeWebhookEvent.objects.create(
                stripe_event_id=event_id,
                event_type=event_type,
                api_version=api_version or "",
                livemode=livemode,
                payload=payload_str,
            )
    except IntegrityError:
        logger.info(
            "[WEBHOOK] Duplicate event %s (%s) — skipping.", event_id, event_type
        )
        return HttpResponse(status=200)

    logger.info(
        "[WEBHOOK] Persisted %s (id=%s) — enqueueing task.", event_type, event_id
    )

    # ------------------------------------------------------------------
    # 4. Process async (Celery) or sync (no Celery)
    # ------------------------------------------------------------------
    from .tasks import process_stripe_webhook_event, process_webhook_event_sync
    from .webhooks import TransientWebhookError

    if getattr(process_stripe_webhook_event, 'delay', None) is not None:
        process_stripe_webhook_event.delay(webhook_event.pk)
    else:
        try:
            process_webhook_event_sync(webhook_event.pk)
        except TransientWebhookError as exc:
            webhook_event.refresh_from_db()
            webhook_event.mark_error(f'Transient: {exc}')
            logger.warning(
                '[WEBHOOK] Sync processing transient error for %s — no retry (Celery not used)',
                webhook_event.stripe_event_id,
            )

    return HttpResponse(status=200)
