#!/usr/bin/env python3
"""Reference receiver for Exlogare outbound webhooks.

Drop this onto a host that can receive HTTPS, set ``EXLOGARE_WEBHOOK_SECRET``
to the secret you got at create-time, and POST traffic from
``/api/integrations/outbound-webhooks`` will be verified and printed.

Usage::

    EXLOGARE_WEBHOOK_SECRET=<your-secret> python3 verify-webhook.py

The script is intentionally dependency-free (stdlib only) so you can drop
it into a tiny VM or a Lambda function as-is.
"""
from __future__ import annotations

import hmac
import json
import os
import sys
import time
from hashlib import sha256
from http.server import BaseHTTPRequestHandler, HTTPServer

SECRET = os.environ.get("EXLOGARE_WEBHOOK_SECRET")
MAX_AGE_SECONDS = 5 * 60  # reject anything older than 5 minutes


def verify(secret: str, header: str, body: bytes, max_age: int = MAX_AGE_SECONDS) -> bool:
    """HMAC-SHA256 verifier, matching ``X-Exlogare-Signature`` format.

    The header is ``t=<unix>,v1=<hex_hmac>``. The HMAC is computed over the
    raw body bytes prefixed with ``"<unix>."`` so a tampered body or a
    replayed delivery (older than ``max_age`` seconds) will not verify.
    """
    parts = dict(p.split("=", 1) for p in header.split(",") if "=" in p)
    try:
        ts = int(parts.get("t", "0"))
    except ValueError:
        return False
    provided = parts.get("v1", "")
    if not provided:
        return False
    if abs(int(time.time()) - ts) > max_age:
        return False
    expected = hmac.new(secret.encode(), f"{ts}.".encode() + body, sha256).hexdigest()
    return hmac.compare_digest(provided, expected)


class Handler(BaseHTTPRequestHandler):
    def do_POST(self) -> None:  # noqa: N802 — stdlib API
        length = int(self.headers.get("Content-Length", "0"))
        body = self.rfile.read(length)
        signature = self.headers.get("X-Exlogare-Signature", "")
        if not SECRET:
            self._reply(500, {"error": "EXLOGARE_WEBHOOK_SECRET not set"})
            return
        if not verify(SECRET, signature, body):
            self._reply(401, {"error": "invalid signature"})
            return
        try:
            payload = json.loads(body or b"{}")
        except json.JSONDecodeError:
            self._reply(400, {"error": "invalid json"})
            return
        print(
            "delivery received:",
            json.dumps(
                {
                    "delivery_id": self.headers.get("X-Exlogare-Delivery"),
                    "event": self.headers.get("X-Exlogare-Event"),
                    "type": payload.get("type"),
                    "tenant": payload.get("tenant_id"),
                    "analysis_id": (payload.get("analysis") or {}).get("id"),
                    "severity": (payload.get("analysis") or {}).get("severity"),
                },
                indent=2,
            ),
        )
        self._reply(200, {"ok": True})

    def _reply(self, status: int, body: dict) -> None:
        encoded = json.dumps(body).encode()
        self.send_response(status)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(encoded)))
        self.end_headers()
        self.wfile.write(encoded)

    def log_message(self, format: str, *args) -> None:  # noqa: A002
        sys.stderr.write("%s - %s\n" % (self.address_string(), format % args))


def main() -> None:
    if not SECRET:
        print(
            "EXLOGARE_WEBHOOK_SECRET environment variable is required",
            file=sys.stderr,
        )
        sys.exit(2)
    port = int(os.environ.get("PORT", "8000"))
    server = HTTPServer(("0.0.0.0", port), Handler)
    print(f"listening on 0.0.0.0:{port} (verify with X-Exlogare-Signature)")
    try:
        server.serve_forever()
    except KeyboardInterrupt:
        pass


if __name__ == "__main__":
    main()
