"""
WebhookEnvelope - Standard wrapper for webhook-out payloads.

Wraps AgenticResult with delivery metadata for routing and tracing
across iPaaS platforms.
"""

from __future__ import annotations

import re
import uuid
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Any, Dict
from urllib.parse import urlparse

from g_gremlin import __version__
from g_gremlin.contracts import AgenticResult


def _utc_timestamp() -> str:
    return datetime.now(timezone.utc).isoformat()


# Patterns for sensitive data that should be redacted
REDACT_PATTERNS = [
    r"--api-key[=\s]+\S+",
    r"--token[=\s]+\S+",
    r"--password[=\s]+\S+",
    r"--secret[=\s]+\S+",
    r"Authorization:\s*Bearer\s+\S+",
    r"[?&]key=\S+",  # Query string API keys
    r"[?&]token=\S+",  # Query string tokens
]

# Compiled patterns for efficiency
_COMPILED_PATTERNS = [re.compile(p, re.IGNORECASE) for p in REDACT_PATTERNS]


def redact_sensitive(text: str) -> str:
    """
    Replace sensitive values with [REDACTED].

    Handles CLI flags, Authorization headers, and query string params.
    """
    result = text
    for pattern in _COMPILED_PATTERNS:
        result = pattern.sub(_redact_match, result)
    return result


def _redact_match(match: re.Match) -> str:
    """Replace matched sensitive value, preserving the key name."""
    matched = match.group()
    # Find the separator (=, space, or :)
    for sep in ["=", " ", ":"]:
        if sep in matched:
            key = matched.split(sep)[0]
            return f"{key}{sep}[REDACTED]"
    return "[REDACTED]"


def mask_url(url: str) -> str:
    """
    Mask URL for logging: show scheme + host only.

    Path segments often contain secrets (Zapier hook IDs, API tokens),
    so we mask the entire path to prevent credential leakage.

    Examples:
        https://hooks.zapier.com/abc123/def456 -> https://hooks.zapier.com/***
        https://api.example.com/webhooks/secret -> https://api.example.com/***
    """
    try:
        parsed = urlparse(url)
        # Only show scheme and host - path often contains secrets
        return f"{parsed.scheme}://{parsed.netloc}/***"
    except Exception:
        return "[INVALID_URL]"


def derive_event_type(result: AgenticResult) -> str:
    """
    Derive event_type from command + status.

    Examples:
        "g-gremlin push" + "success" -> "push.success"
        "g-gremlin sync all" + "partial" -> "sync_all.partial"
    """
    cmd = result.command
    # Strip "g-gremlin " prefix if present
    if cmd.startswith("g-gremlin "):
        cmd = cmd[len("g-gremlin ") :]
    # Replace spaces with underscores
    cmd = cmd.replace(" ", "_")
    return f"{cmd}.{result.status}"


@dataclass
class WebhookEnvelope:
    """
    Standard envelope for webhook-out payloads.

    Contains routing fields (event_type, correlation_id) and delivery
    metadata for tracing. The payload is the serialized AgenticResult.
    """

    # Core routing fields (what downstream cares about)
    event_type: str  # e.g., "sync.completed", "push.success"
    correlation_id: str  # Copied from AgenticResult

    # Delivery metadata
    delivery_id: str = field(default_factory=lambda: str(uuid.uuid4()))
    source: str = "g-gremlin"
    source_version: str = field(default_factory=lambda: __version__)
    timestamp: str = field(default_factory=_utc_timestamp)

    # Payload (serialized AgenticResult)
    payload: Dict[str, Any] = field(default_factory=dict)

    # Tracking (not for downstream)
    attempt: int = 1
    max_attempts: int = 3

    def to_dict(self) -> Dict[str, Any]:
        """Serialize envelope to dict for JSON encoding."""
        return {
            "$schema": "WebhookEnvelope/v1",
            "event_type": self.event_type,
            "correlation_id": self.correlation_id,
            "delivery_id": self.delivery_id,
            "source": self.source,
            "source_version": self.source_version,
            "timestamp": self.timestamp,
            "payload": self.payload,
        }

    @classmethod
    def from_agentic_result(
        cls,
        result: AgenticResult,
        max_attempts: int = 3,
    ) -> "WebhookEnvelope":
        """
        Create envelope from AgenticResult.

        Derives event_type automatically and copies correlation_id.
        """
        return cls(
            event_type=derive_event_type(result),
            correlation_id=result.correlation_id,
            payload=result.to_dict(),
            max_attempts=max_attempts,
        )
