-- v1.0.7 item E: raw-payload audit log for every Hyperswitch webhook
-- reaching our endpoint. Captures both legitimate deliveries and
-- attack attempts (invalid signatures, malformed bodies) — the insert
-- happens regardless of signature-valid / processing-success status,
-- so a forensics query after "something weird happened last Tuesday"
-- has the actual bytes to look at.
--
-- Shape decisions:
--
--   payload TEXT — Hyperswitch sends JSON; TEXT is readable in psql
--   without base64-decoding and plenty fast at our volumes. Invalid
--   UTF-8 is rejected at INSERT time — that class of "attack" is a
--   grossly malformed probe where we have the header + ip + timing
--   anyway, no value in storing the binary payload.
--
--   signature_valid BOOLEAN — HMAC verification outcome. Partial
--   index below makes "attempts with invalid signature last 24h"
--   cheap for forensics.
--
--   processing_result TEXT — 'ok' on successful dispatch, 'error: <msg>'
--   on processing failure (after signature was valid), 'skipped' if
--   the handler declined for another reason, 'signature_invalid' if
--   rejected at the signature gate.
--
--   source_ip / user_agent / request_id — forensics essentials.
--   request_id is captured from Hyperswitch's X-Request-Id header if
--   sent, else the handler generates a UUID so every row has a value
--   correlatable against VEZA's structured logs.
--
--   event_type — pulled from the payload JSON via a best-effort
--   extract; NULL if the payload isn't valid JSON or doesn't carry
--   an event_type field. Useful for "how many dispute.* events have
--   we seen this month" — item P1.6 (disputes) rides along on this
--   log without needing its own handler yet.
--
-- Retention: 90 days by default, swept by CleanupHyperswitchWebhookLog
-- (internal/jobs). Configurable via HYPERSWITCH_WEBHOOK_LOG_RETENTION_DAYS.

CREATE TABLE IF NOT EXISTS hyperswitch_webhook_log (
    id                  UUID PRIMARY KEY DEFAULT gen_random_uuid(),
    received_at         TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    payload             TEXT NOT NULL,
    signature_valid     BOOLEAN NOT NULL,
    signature_header    TEXT,
    processing_result   TEXT NOT NULL,
    event_type          TEXT,
    source_ip           TEXT,
    user_agent          TEXT,
    request_id          TEXT NOT NULL
);

-- Received-at ordering index: "what did we receive in the last hour"
-- is the single most common operational query. Cheap, indexed by
-- default PK on id but adding the timestamp index keeps retention
-- sweeps and forensics scans well-planned.
CREATE INDEX IF NOT EXISTS idx_hyperswitch_webhook_log_received_at
    ON hyperswitch_webhook_log(received_at DESC);

-- Partial index on invalid signatures — "show me attack attempts".
-- Partial keeps the index tiny on the common case (valid sigs) and
-- makes the forensics query instantaneous on the rare case.
CREATE INDEX IF NOT EXISTS idx_hyperswitch_webhook_log_signature_invalid
    ON hyperswitch_webhook_log(received_at DESC)
    WHERE signature_valid = false;

-- request_id is required-NOT-NULL at the column level, so an index
-- on it is just for "correlate this Veza log line with the webhook
-- row". Non-unique because retries with the same request_id could
-- land multiple rows (e.g., Hyperswitch redelivers a webhook).
CREATE INDEX IF NOT EXISTS idx_hyperswitch_webhook_log_request_id
    ON hyperswitch_webhook_log(request_id);
