-- Migration 027 — TOTP two-factor auth.
--
-- totp_secret holds the base32 shared secret once enrollment is confirmed
-- (NULL while disabled or mid-enrollment). totp_enabled flips true only after
-- the user verifies their first code, so a half-finished enrollment never locks
-- anyone out. Recovery codes are one-time bcrypt-hashed fallbacks; used_at marks
-- a code as spent.

ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_secret  TEXT;
ALTER TABLE users ADD COLUMN IF NOT EXISTS totp_enabled BOOLEAN NOT NULL DEFAULT FALSE;

CREATE TABLE IF NOT EXISTS user_recovery_codes (
  id         UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
  user_id    UUID NOT NULL REFERENCES users ON DELETE CASCADE,
  code_hash  TEXT NOT NULL,
  used_at    TIMESTAMPTZ,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_user_recovery_codes_user ON user_recovery_codes(user_id);
