-- 003_outbox_state_machine.sql
-- Enforce state machine constraints and valid transitions
-- Prevents invalid status transitions (e.g., FINALIZED -> QUEUED)

ALTER TABLE dual_ledger_outbox
  ADD CONSTRAINT dual_ledger_outbox_status_check
  CHECK (status IN ('QUEUED','SENT','ACKED','FINALIZED','FAILED'));

CREATE OR REPLACE FUNCTION enforce_outbox_status_transition()
RETURNS trigger AS $$
DECLARE
  allowed boolean := false;
BEGIN
  -- No-op if status unchanged
  IF OLD.status = NEW.status THEN
    RETURN NEW;
  END IF;

  -- Allowed transitions:
  -- QUEUED -> SENT | FAILED
  -- SENT -> ACKED | FAILED
  -- ACKED -> FINALIZED | FAILED
  -- FAILED -> QUEUED (retry) | FAILED (no change)
  IF OLD.status = 'QUEUED' AND NEW.status IN ('SENT','FAILED') THEN
    allowed := true;
  ELSIF OLD.status = 'SENT' AND NEW.status IN ('ACKED','FAILED') THEN
    allowed := true;
  ELSIF OLD.status = 'ACKED' AND NEW.status IN ('FINALIZED','FAILED') THEN
    allowed := true;
  ELSIF OLD.status = 'FAILED' AND NEW.status IN ('QUEUED','FAILED') THEN
    allowed := true;
  END IF;

  IF NOT allowed THEN
    RAISE EXCEPTION 'Invalid outbox transition: % -> %', OLD.status, NEW.status;
  END IF;

  RETURN NEW;
END;
$$ LANGUAGE plpgsql;

DROP TRIGGER IF EXISTS dual_ledger_outbox_status_transition ON dual_ledger_outbox;
CREATE TRIGGER dual_ledger_outbox_status_transition
BEFORE UPDATE ON dual_ledger_outbox
FOR EACH ROW EXECUTE FUNCTION enforce_outbox_status_transition();
