import hashlib
import hmac
import json
import logging
import time
from typing import Any

import httpx

from src.apps.payment_providers.helpers.credentials import decrypt_credential
from src.apps.developer.models.merchant_webhook_endpoint import MerchantWebhookEndpoint
from src.apps.developer.models.merchant_webhook_delivery import MerchantWebhookDelivery

logger = logging.getLogger(__name__)


def _sign_payload(raw_secret: str, timestamp: int, json_body: str) -> str:
    """HMAC-SHA256(raw_secret, f"{timestamp}.{json_body}")"""
    message = f"{timestamp}.{json_body}"
    return hmac.new(raw_secret.encode(), message.encode(), hashlib.sha256).hexdigest()


async def dispatch_webhook(
    db,
    endpoint: MerchantWebhookEndpoint,
    event_type: str,
    event_id: str,
    merchant_id: int,
    data: dict[str, Any],
    is_retry: bool = False,
) -> None:
    """
    Dispatch a signed webhook POST to endpoint.url.
    Creates a MerchantWebhookDelivery record regardless of outcome.
    Never raises.
    """
    timestamp = int(time.time())
    payload = {
        "event_type": event_type,
        "event_id": event_id,
        "timestamp": timestamp,
        "merchant_id": merchant_id,
        "data": data,
    }
    json_body = json.dumps(payload, separators=(",", ":"), sort_keys=True)

    raw_secret = decrypt_credential(endpoint.signing_secret_encrypted)
    signature = _sign_payload(raw_secret, timestamp, json_body)
    sig_header = f"t={timestamp},v1={signature}"

    delivery = MerchantWebhookDelivery(
        webhook_endpoint_id=endpoint.id,
        merchant_id=merchant_id,
        event_type=event_type,
        event_id=event_id,
        payload=payload,
    )

    success = False
    try:
        async with httpx.AsyncClient(timeout=5.0) as client:
            resp = await client.post(
                endpoint.url,
                content=json_body,
                headers={
                    "Content-Type": "application/json",
                    "X-HubWallet-Signature": sig_header,
                    "X-HubWallet-Event": event_type,
                },
            )
        delivery.response_status = resp.status_code
        delivery.response_body = resp.text[:1000]

        if 200 <= resp.status_code < 300:
            success = True
            from datetime import datetime, timezone
            delivery.delivered_at = datetime.now(timezone.utc)
            endpoint.failure_count = 0
            endpoint.last_triggered_at = datetime.now(timezone.utc)
        else:
            from datetime import datetime, timezone
            delivery.failed_at = datetime.now(timezone.utc)
            endpoint.failure_count = (endpoint.failure_count or 0) + 1

    except Exception as exc:
        logger.warning("dispatch_webhook failed for endpoint %d: %s", endpoint.id, exc)
        from datetime import datetime, timezone
        delivery.failed_at = datetime.now(timezone.utc)
        endpoint.failure_count = (endpoint.failure_count or 0) + 1

    # Auto-disable after 10 consecutive failures
    if (endpoint.failure_count or 0) >= 10:
        endpoint.is_active = False
        logger.warning("Webhook endpoint %d auto-disabled after 10 consecutive failures", endpoint.id)

    # Only queue Celery retry on initial dispatch failure (not is_retry — Celery handles that)
    if not success and not is_retry:
        try:
            from src.apps.developer.tasks import retry_webhook_delivery
            retry_webhook_delivery.apply_async(
                args=[endpoint.id, event_type, event_id, merchant_id, data],
                countdown=60,
            )
        except Exception as exc:
            logger.warning("Failed to enqueue webhook retry task: %s", exc)

    db.add(delivery)
    db.commit()
