"""Shared Cloud Function helpers — structured logging, email alerts, error wrapper.

Every scheduled Cloud Function uses ``cloud_function_wrapper`` to handle
errors uniformly.  ``log_action`` emits structured JSON for SOC 2 CC6.5
audit trail.  ``send_alert_email`` fires failure alerts via Postmark.

Contract: all helpers are fire-and-forget — they never raise to callers.
"""

from __future__ import annotations

import functools
import json
import logging
import sys
import traceback
from datetime import UTC, datetime
from typing import Any

_log = logging.getLogger(__name__)

_ALERT_TO = "alerts@complyform.dev"


def cloud_function_wrapper(func_name: str):
    """Decorator for Cloud Functions.

    1. Wrap entire function in try/except
    2. On success: log structured JSON summary
    3. On error: log structured JSON error, send failure alert to alerts@complyform.dev
    4. ALWAYS return 200 to Cloud Scheduler (prevent retry storms)
    """

    def decorator(func):  # noqa: ANN001, ANN202
        @functools.wraps(func)
        async def wrapper(request: Any) -> dict[str, Any]:
            started = datetime.now(UTC)
            try:
                result = await func(request)
                elapsed = (datetime.now(UTC) - started).total_seconds()
                log_action(
                    customer_id=None,
                    action=f"{func_name}_complete",
                    elapsed_seconds=elapsed,
                    **(result if isinstance(result, dict) else {}),
                )
                return {"status": "ok", "function": func_name}
            except Exception as exc:
                elapsed = (datetime.now(UTC) - started).total_seconds()
                tb = traceback.format_exc()
                log_action(
                    customer_id=None,
                    action=f"{func_name}_error",
                    error=str(exc),
                    traceback=tb,
                    elapsed_seconds=elapsed,
                )
                try:
                    await send_alert_email(
                        subject=f"[ComplyForm] {func_name} failed",
                        body=f"Function: {func_name}\nError: {exc}\n\n{tb}",
                    )
                except Exception:
                    _log.exception("Failed to send alert email")
                # Always return 200 to prevent Cloud Scheduler retry storms
                return {"status": "error", "function": func_name, "error": str(exc)}

        return wrapper

    return decorator


def log_action(
    customer_id: str | None,
    action: str,
    record_count: int = 0,
    error: str | None = None,
    **extra: Any,
) -> None:
    """Structured JSON log entry for SOC 2 CC6.5 audit trail."""
    entry: dict[str, Any] = {
        "timestamp": datetime.now(UTC).isoformat(),
        "action": action,
        "customer_id": customer_id,
        "record_count": record_count,
    }
    if error is not None:
        entry["error"] = error
    entry.update(extra)
    # Emit to stderr as structured JSON
    print(json.dumps(entry, default=str), file=sys.stderr)


async def send_alert_email(
    subject: str,
    body: str,
    to: str = _ALERT_TO,
) -> None:
    """Send alert via Postmark. Fire-and-forget — never raises.

    Uses existing postmark-server-token from Secret Manager.
    """
    try:
        import httpx

        from backend.billing.email_sender import _get_postmark_token

        token = await _get_postmark_token()
        headers = {
            "Accept": "application/json",
            "Content-Type": "application/json",
            "X-Postmark-Server-Token": token,
        }
        payload = {
            "From": "notifications@complyform.dev",
            "To": to,
            "Subject": subject,
            "TextBody": body,
        }
        async with httpx.AsyncClient(timeout=15.0) as client:
            await client.post(
                "https://api.postmarkapp.com/email",
                json=payload,
                headers=headers,
            )
    except Exception:
        _log.exception("send_alert_email failed (fire-and-forget)")
