# Verify a RelationsAI-Signature header. Python 3.9+ standard library only.
#
# In your server:   from verify import verify_signature
# To check a saved delivery from the command line:
#   RELATIONSAI_WEBHOOK_SECRET=whsec_... SIGNATURE='t=...,v1=...' python verify.py < body.json
import hashlib
import hmac
import os
import re
import sys
import time


def verify_signature(raw_body, header, secrets, tolerance_seconds=300, now=None):
    """Return (valid, reason).

    raw_body: the request body exactly as received (bytes), before any JSON parsing.
    header:   the RelationsAI-Signature header value.
    secrets:  your endpoint's signing secret(s), each the full whsec_... string.
    """
    t = None
    signatures = []
    for part in (header or "").split(","):
        key, _, value = part.strip().partition("=")
        if key == "t" and value.isdigit():
            t = int(value)
        elif key == "v1" and re.fullmatch(r"[0-9a-f]{64}", value):
            signatures.append(value)
    if t is None:
        return False, "no timestamp (t=) in the header"
    if not signatures:
        return False, "no v1= signature in the header"
    if abs((time.time() if now is None else now) - t) > tolerance_seconds:
        return False, "timestamp outside the tolerance"

    body = raw_body if isinstance(raw_body, bytes) else raw_body.encode("utf-8")
    for secret in secrets:
        expected = hmac.new(secret.encode("utf-8"), f"{t}.".encode() + body, hashlib.sha256).hexdigest()
        # Several v1 values are present during a secret rotation: any match is enough.
        if any(hmac.compare_digest(expected, signature) for signature in signatures):
            return True, ""
    return False, "no signature matches (wrong secret, or the body was changed)"


if __name__ == "__main__":
    secrets = [s for s in os.environ.get("RELATIONSAI_WEBHOOK_SECRET", "").split(",") if s]
    if not secrets or not os.environ.get("SIGNATURE"):
        sys.exit("Set RELATIONSAI_WEBHOOK_SECRET and SIGNATURE, and pipe the raw body to stdin")
    # TOLERANCE_SECONDS lets you check an old saved delivery here; never raise it in your server.
    valid, reason = verify_signature(
        sys.stdin.buffer.read(),
        os.environ["SIGNATURE"],
        secrets,
        tolerance_seconds=float(os.environ.get("TOLERANCE_SECONDS", "300")),
    )
    print("valid" if valid else f"invalid: {reason}")
    sys.exit(0 if valid else 1)
