-- ============================================================================
-- 0004_credentials_vault.sql
-- ----------------------------------------------------------------------------
-- Per-user encrypted credential store for surface adapters.
--
-- Dual-mode auth: 'cookie' (paste-once, no captcha exposure) and 'credentials'
-- (full re-login with username/password/totp). Both paths converge on the
-- same Playwright runtime once authenticated.
--
-- Encryption: AES-256-GCM at-rest in *_enc columns. DEK is delivered per-
-- session by the custodian process on black via SSE; falls back to
-- CREDENTIALS_ENCRYPTION_KEY env in dev. Encrypted blob format:
--   "<iv_hex>:<authTag_hex>:<ciphertext_hex>"
--
-- Spec source: _engineering-credentials-vault.md §"Step 1 — schema port".
-- ============================================================================

CREATE TYPE credential_platform_type AS ENUM (
  'escort',
  'content',
  'screening',
  'reviews',
  'socials',
  'messaging',
  'travel',
  'other'
);

CREATE TYPE credential_auth_mode AS ENUM ('cookie', 'credentials');

CREATE TABLE credentials (
  id                UUID PRIMARY KEY DEFAULT gen_random_uuid(),
  user_id           UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE,
  org_id            UUID NULL     REFERENCES orgs(id)  ON DELETE CASCADE,

  -- Identity
  platform          TEXT NOT NULL,                        -- canonical surface id (e.g. 'tryst')
  platform_name     TEXT,                                 -- display name
  platform_type     credential_platform_type NOT NULL DEFAULT 'escort',
  category          TEXT,                                 -- soft taxonomy bucket
  url               TEXT,                                 -- login page or root url

  -- Auth-mode discriminator
  auth_mode         credential_auth_mode NOT NULL DEFAULT 'credentials',

  -- credentials-mode fields (NULL when auth_mode='cookie')
  username          TEXT,
  password_enc      TEXT,
  totp_secret_enc   TEXT,

  -- cookie-mode fields (NULL when auth_mode='credentials')
  cookie_blob_enc   TEXT,

  -- shared
  note              TEXT,
  sort_order        INT  NOT NULL DEFAULT 0,
  is_archived       BOOLEAN NOT NULL DEFAULT FALSE,        -- mode-switch keeps prior auth for revert
  last_verified_at  TIMESTAMPTZ NULL,                      -- last time adapter confirmed the session works
  created_at        TIMESTAMPTZ NOT NULL DEFAULT now(),
  updated_at        TIMESTAMPTZ NOT NULL DEFAULT now(),

  CONSTRAINT credentials_auth_mode_shape CHECK (
    (auth_mode = 'credentials'
       AND username        IS NOT NULL
       AND password_enc    IS NOT NULL
       AND cookie_blob_enc IS NULL)
    OR
    (auth_mode = 'cookie'
       AND cookie_blob_enc IS NOT NULL
       AND password_enc    IS NULL
       AND totp_secret_enc IS NULL)
  )
);

-- One active row per (user, platform, username). Archived rows ignored;
-- this lets mode-switch keep prior creds without unique conflicts.
CREATE UNIQUE INDEX idx_credentials_user_platform_username_active
  ON credentials (user_id, lower(platform), lower(coalesce(username, '__cookie__')))
  WHERE NOT is_archived;

CREATE INDEX idx_credentials_user_platform
  ON credentials (user_id, platform)
  WHERE NOT is_archived;

CREATE INDEX idx_credentials_org_platform
  ON credentials (org_id, platform)
  WHERE org_id IS NOT NULL AND NOT is_archived;

CREATE TRIGGER trg_credentials_updated_at
  BEFORE UPDATE ON credentials
  FOR EACH ROW EXECUTE FUNCTION touch_updated_at();

-- ----------------------------------------------------------------------------
-- RLS — tenant isolation
-- ----------------------------------------------------------------------------

ALTER TABLE credentials ENABLE ROW LEVEL SECURITY;

CREATE POLICY tenant_isolation_credentials ON credentials
  USING (
    user_id = current_setting('app.user_id', true)::uuid
    OR (org_id IS NOT NULL AND org_id = current_setting('app.org_id', true)::uuid)
  )
  WITH CHECK (
    user_id = current_setting('app.user_id', true)::uuid
    OR (org_id IS NOT NULL AND org_id = current_setting('app.org_id', true)::uuid)
  );

-- ----------------------------------------------------------------------------
-- Per-user attribution salt (referenced by _engineering-surface-metrics §9)
-- ----------------------------------------------------------------------------

ALTER TABLE users
  ADD COLUMN IF NOT EXISTS attribution_salt BYTEA NULL;

COMMENT ON COLUMN users.attribution_salt IS
  'Per-user 32-byte salt for identifier_hash in prospect_touchpoints. Generated lazily on first touchpoint write. NEVER exposed via API.';
