"""Alert delivery for drift monitoring — Postmark email + HMAC-signed webhook.

Email and webhook failures are independent: one failing does not suppress
the other.
"""

from __future__ import annotations

import asyncio
import hashlib
import hmac
import logging
import time

import httpx

from backend.drift.models import (
    DriftAlertPayload,
    DriftDetectionResult,
)

_log = logging.getLogger(__name__)

_POSTMARK_URL = "https://api.postmarkapp.com/email/withTemplate"
_FROM_ADDRESS = "noreply@complyform.dev"
_WEBHOOK_USER_AGENT = "ComplyForm-Drift/1.0"
_RETRY_DELAYS = [1, 4, 16]  # exponential backoff: 3 attempts


async def _get_postmark_token() -> str:
    """Retrieve Postmark server token from Secret Manager."""
    from google.cloud import secretmanager

    client = secretmanager.SecretManagerServiceClient()
    name = "projects/complyform-prod/secrets/postmark-server-token/versions/latest"
    response = client.access_secret_version(request={"name": name})
    return response.payload.data.decode("utf-8")


async def _send_email_alert(
    *,
    result: DriftDetectionResult,
    to_email: str,
) -> bool:
    """Send drift alert email via Postmark. Returns True on success."""
    try:
        token = await _get_postmark_token()
    except Exception:
        _log.exception("Failed to retrieve Postmark token")
        return False

    headers = {
        "Accept": "application/json",
        "Content-Type": "application/json",
        "X-Postmark-Server-Token": token,
    }
    payload = {
        "From": _FROM_ADDRESS,
        "To": to_email,
        "TemplateAlias": "drift-alert",
        "TemplateModel": {
            "project_label": result.project_label,
            "old_score": result.previous_score,
            "new_score": result.new_score,
            "score_delta": result.score_delta,
            "top_findings": [f.model_dump() for f in result.top_findings],
            "dashboard_url": (
                f"https://dashboard.complyform.dev/projects/{result.project_id}"
            ),
        },
    }

    for attempt in range(2):
        try:
            async with httpx.AsyncClient(timeout=15.0) as client:
                resp = await client.post(_POSTMARK_URL, json=payload, headers=headers)

            if resp.status_code == 200:
                return True

            if resp.status_code >= 500 and attempt == 0:
                _log.warning(
                    "Postmark 5xx (%s) for drift-alert, retrying",
                    resp.status_code,
                )
                await asyncio.sleep(5)
                continue

            _log.error(
                "Postmark error %s for drift-alert: %s",
                resp.status_code,
                resp.text,
            )
            return False

        except Exception:
            if attempt == 0:
                _log.exception("Postmark request failed, retrying")
                await asyncio.sleep(5)
                continue
            _log.exception("Postmark request failed after retry")
            return False

    return False


async def _send_webhook_alert(
    *,
    payload: DriftAlertPayload,
    webhook_url: str,
    webhook_secret: bytes,
) -> bool:
    """Send HMAC-signed webhook. Returns True on success, False after retries.

    Headers:
      X-ComplyForm-Signature: HMAC-SHA256 hex digest
      X-ComplyForm-Timestamp: Unix timestamp string
      Content-Type: application/json
      User-Agent: ComplyForm-Drift/1.0
    """
    timestamp = str(int(time.time()))
    body_json = payload.model_dump_json()
    signed_message = f"{timestamp}.{body_json}".encode()
    signature = hmac.new(webhook_secret, signed_message, hashlib.sha256).hexdigest()

    headers = {
        "X-ComplyForm-Signature": signature,
        "X-ComplyForm-Timestamp": timestamp,
        "Content-Type": "application/json",
        "User-Agent": _WEBHOOK_USER_AGENT,
    }

    for attempt, delay in enumerate(_RETRY_DELAYS):
        try:
            async with httpx.AsyncClient(timeout=15.0) as client:
                resp = await client.post(
                    webhook_url, content=body_json, headers=headers
                )

            if 200 <= resp.status_code < 300:
                return True

            _log.warning(
                "Webhook %s returned %s (attempt %d/%d)",
                webhook_url,
                resp.status_code,
                attempt + 1,
                len(_RETRY_DELAYS),
            )

        except Exception:
            _log.exception(
                "Webhook request failed (attempt %d/%d)",
                attempt + 1,
                len(_RETRY_DELAYS),
            )

        if attempt < len(_RETRY_DELAYS) - 1:
            await asyncio.sleep(delay)

    _log.error(
        "Webhook delivery to %s failed after %d attempts",
        webhook_url,
        len(_RETRY_DELAYS),
    )
    return False


async def send_drift_alert(
    *,
    result: DriftDetectionResult,
    alert_config: dict,  # type: ignore[type-arg]
    customer_email: str,
    webhook_secret: bytes | None,
) -> None:
    """Send drift alert via email and/or webhook.

    1. Always send Postmark email to alert_email or customer_email.
    2. If webhook configured: send HMAC-signed POST.
    3. Failures are independent — one does not block the other.
    """
    to_email = alert_config.get("alert_email") or customer_email

    # Fire email
    email_ok = await _send_email_alert(result=result, to_email=to_email)
    if not email_ok:
        _log.error("Drift email alert failed for project %s", result.project_id)

    # Fire webhook if configured
    webhook_url = alert_config.get("webhook_url")
    if webhook_url and webhook_secret:
        payload = DriftAlertPayload(
            project_label=result.project_label,
            cloud=result.cloud,
            old_score=result.previous_score,
            new_score=result.new_score,
            score_delta=result.score_delta,
            top_findings=result.top_findings[:5],
            dashboard_url=(
                f"https://dashboard.complyform.dev/projects/{result.project_id}"
            ),
            timestamp=str(int(time.time())),
        )
        webhook_ok = await _send_webhook_alert(
            payload=payload,
            webhook_url=webhook_url,
            webhook_secret=webhook_secret,
        )
        if not webhook_ok:
            _log.error(
                "Drift webhook alert failed for project %s",
                result.project_id,
            )
