-- Migration 020: Add cryptographic hash chain columns to audit_logs
--
-- SOC 2 CC7.2 — Audit Log Integrity
--
-- Adds Merkle-style hash chain columns so that every audit event is
-- cryptographically linked to the previous one. Any deletion, modification,
-- or insertion of events will cause chain verification to fail, providing
-- tamper-evident logging.
--
-- Chain format:
--   hash = SHA-256(eventId || timestamp.toISOString() || action || outcome
--                  || agentId || organizationId || previousHash)
--   previous_hash references the `hash` of the chronologically preceding event.
--   The first event uses previousHash = '' (empty string sentinel).
--
-- Column backfill:
--   Existing rows are seeded with hash = '' and previous_hash = '' as an
--   initial seed. The actual hashes will be computed and back-filled by
--   AuditChainVerificationJob / EncryptionService on the next read-write cycle.

-- ── 1. Add hash chain columns ────────────────────────────────────────────────
ALTER TABLE audit_events
  ADD COLUMN IF NOT EXISTS hash          VARCHAR(64) NOT NULL DEFAULT '',
  ADD COLUMN IF NOT EXISTS previous_hash VARCHAR(64) NOT NULL DEFAULT '';

-- ── 2. Index for chain traversal (ascending time order) ─────────────────────
CREATE INDEX IF NOT EXISTS idx_audit_events_chain_order
  ON audit_events (timestamp ASC, event_id ASC);

-- ── 3. Immutability trigger — prevent UPDATE and DELETE ──────────────────────
CREATE OR REPLACE FUNCTION audit_events_immutable()
RETURNS TRIGGER LANGUAGE plpgsql AS $$
BEGIN
  IF TG_OP = 'UPDATE' THEN
    RAISE EXCEPTION
      'audit_events is immutable: UPDATE on event_id % is not permitted.',
      OLD.event_id;
  END IF;

  IF TG_OP = 'DELETE' THEN
    RAISE EXCEPTION
      'audit_events is immutable: DELETE on event_id % is not permitted.',
      OLD.event_id;
  END IF;

  RETURN NULL;
END;
$$;

DROP TRIGGER IF EXISTS trg_audit_events_immutable ON audit_events;

CREATE TRIGGER trg_audit_events_immutable
  BEFORE UPDATE OR DELETE ON audit_events
  FOR EACH ROW EXECUTE FUNCTION audit_events_immutable();

-- ── 4. Backfill existing rows ────────────────────────────────────────────────
-- Set empty sentinels for pre-chain rows.
-- The trigger above blocks UPDATE, so we temporarily disable it for this
-- one-time backfill. The actual cryptographic hashes will be computed
-- by the application on the next chain verification run.
ALTER TABLE audit_events DISABLE TRIGGER trg_audit_events_immutable;

UPDATE audit_events
SET hash = '', previous_hash = ''
WHERE hash = '';

ALTER TABLE audit_events ENABLE TRIGGER trg_audit_events_immutable;
