"""Webhook registry for triggering chains via external HTTP calls."""

import hashlib
import hmac
import json
import uuid
from datetime import datetime, timezone
from pathlib import Path


class WebhookRegistry:
    """Manages webhook configurations stored in ~/.brynq/webhooks.json."""

    def __init__(self, config_dir: str | Path | None = None) -> None:
        self._config_dir = Path(config_dir) if config_dir else Path.home() / ".brynq"
        self._config_file = self._config_dir / "webhooks.json"
        self._webhooks: list[dict] = self._load()

    def _load(self) -> list[dict]:
        """Load webhook configs from disk, creating file if missing."""
        if not self._config_file.exists():
            self._config_dir.mkdir(parents=True, exist_ok=True)
            self._config_file.write_text(json.dumps([], indent=2))
            return []
        data = json.loads(self._config_file.read_text())
        if not isinstance(data, list):
            return []
        return data

    def _save(self) -> None:
        """Persist current webhooks to disk."""
        self._config_dir.mkdir(parents=True, exist_ok=True)
        self._config_file.write_text(json.dumps(self._webhooks, indent=2))

    def register(self, name: str, chain_template: str, auth_token: str) -> str:
        """Register a new webhook and return its generated ID.

        Args:
            name: Human-readable name for the webhook.
            chain_template: Chain template string to execute when triggered.
            auth_token: Secret token for authenticating incoming requests.

        Returns:
            The generated uuid4 ID for this webhook.
        """
        webhook_id = str(uuid.uuid4())
        auth_token_hash = hashlib.sha256(auth_token.encode()).hexdigest()
        entry = {
            "id": webhook_id,
            "name": name,
            "chain_template": chain_template,
            "auth_token_hash": auth_token_hash,
            "created_at": datetime.now(timezone.utc).isoformat(),
        }
        self._webhooks.append(entry)
        self._save()
        return webhook_id

    def validate_auth(self, webhook_id: str, provided_token: str) -> bool:
        """Validate an incoming auth token against the stored hash.

        Args:
            webhook_id: The webhook ID to authenticate against.
            provided_token: The plaintext token provided by the caller.

        Returns:
            True if the token matches, False if not found or mismatch.
        """
        for wh in self._webhooks:
            if wh["id"] == webhook_id:
                provided_hash = hashlib.sha256(provided_token.encode()).hexdigest()
                return hmac.compare_digest(provided_hash, wh["auth_token_hash"])
        return False

    def list_all(self) -> list[dict]:
        """Return all registered webhooks, excluding auth_token_hash.

        Returns:
            List of dicts with keys: id, name, chain_template, created_at.
        """
        return [
            {k: v for k, v in wh.items() if k != "auth_token_hash"}
            for wh in self._webhooks
        ]

    def delete(self, webhook_id: str) -> bool:
        """Delete a webhook by ID.

        Args:
            webhook_id: The uuid4 ID of the webhook to delete.

        Returns:
            True if the webhook was found and deleted, False if not found.
        """
        for i, wh in enumerate(self._webhooks):
            if wh.get("id") == webhook_id:
                self._webhooks.pop(i)
                self._save()
                return True
        return False

    def get(self, webhook_id: str) -> dict | None:
        """Return the webhook config for a given ID, or None if not found.

        The auth_token_hash field is excluded from the returned dict.

        Args:
            webhook_id: The uuid4 ID of the webhook to look up.

        Returns:
            Dict with keys: id, name, chain_template, created_at, or None.
        """
        for wh in self._webhooks:
            if wh.get("id") == webhook_id:
                return {k: v for k, v in wh.items() if k != "auth_token_hash"}
        return None

    def log_trigger(
        self,
        webhook_id: str,
        status: str,
        latency_ms: float,
        error: str | None = None,
    ) -> None:
        """Append a trigger log entry to ~/.brynq/webhook_logs.jsonl.

        Args:
            webhook_id: The webhook that was triggered.
            status: Outcome status (e.g. "success", "error").
            latency_ms: Execution latency in milliseconds.
            error: Optional error message if the trigger failed.
        """
        entry = {
            "webhook_id": webhook_id,
            "status": status,
            "latency_ms": latency_ms,
            "error": error,
            "timestamp": datetime.now(timezone.utc).isoformat(),
        }
        log_file = self._config_dir / "webhook_logs.jsonl"
        self._config_dir.mkdir(parents=True, exist_ok=True)
        with open(log_file, "a", encoding="utf-8") as f:
            f.write(json.dumps(entry) + "\n")

    def get_logs(self, webhook_id: str, limit: int = 50) -> list[dict]:
        """Read webhook trigger logs filtered by webhook ID.

        Args:
            webhook_id: The webhook ID to filter logs for.
            limit: Maximum number of entries to return (default 50).

        Returns:
            List of log dicts for the given webhook, newest first,
            capped at ``limit`` entries.
        """
        log_file = self._config_dir / "webhook_logs.jsonl"
        if not log_file.exists():
            return []
        entries: list[dict] = []
        with open(log_file, "r", encoding="utf-8") as f:
            for line in f:
                line = line.strip()
                if not line:
                    continue
                try:
                    entry = json.loads(line)
                except json.JSONDecodeError:
                    continue
                if entry.get("webhook_id") == webhook_id:
                    entries.append(entry)
        entries.sort(key=lambda e: e.get("timestamp", ""), reverse=True)
        return entries[:limit]
