-- §6.1 / §6.2 / §14.1 / v0.8.0: open beta-access request flow (roadmap item #6).
--
-- This release replaces v0.3.0's `allowed_emails` allowlist as the
-- admission control. Anyone with a valid email can sign in via the
-- v0.7.0 OTC flow; a fresh user lands in `permission_state='pending'`
-- until an admin grants access. The first-OTC flow captures three
-- profile fields (first name, last name, free-text "why I should be
-- included in the beta") that the admin sees when triaging the
-- request queue. The `allowed_emails` table stays in the schema as a
-- fast-path bypass — populated rows are still readable by the
-- existing admin UI; the OTC `/request` handler no longer consults
-- it. v0.9.0's admin user-management page will replace the
-- allowlist UI entirely.
--
-- Schema additions:
--
--   * `permission_state` — three-state CHECK: 'pending' | 'granted' |
--     'revoked'. Default 'granted' so every row at migration time
--     passes through unaffected; only newly provisioned OTC users
--     land in 'pending' (the OTC verify path sets the column
--     explicitly on a fresh row, per `app/otc.py`). 'revoked' is the
--     admin gesture for an account that earned a grant then later
--     lost it; v0.8.0 doesn't surface a revoke UI, but the schema
--     slot is here so v0.9.0's admin user-management page can flip
--     the column without another migration.
--
--   * `first_name`, `last_name` — nullable TEXT. Captured on the
--     first OTC sign-in via `POST /auth/me/beta-request`. Existing
--     rows (OAuth-era users, OTC users provisioned in v0.7.0) carry
--     NULL through the migration; the admin queue treats an
--     unpopulated capture as "auto-grandfathered" since the row's
--     `permission_state` is already 'granted'.
--
--   * `beta_request_reason` — nullable TEXT. The free-text "why I
--     should be included" from the capture form. Bounded to ~4000
--     chars at the endpoint layer (no DB-level constraint —
--     SQLite's TEXT is unbounded).
--
--   * `permission_decided_by` — nullable INTEGER. The `users.id` of
--     the admin who flipped `permission_state` from 'pending' to
--     'granted' (or 'granted' to 'revoked'). NULL for grandfathered
--     rows (they were never decided — they passed through at
--     migration). ON DELETE SET NULL because losing the admin row
--     should not cascade-delete the user whose access they granted.
--
--   * `permission_decided_at` — nullable TEXT timestamp (ISO 8601,
--     same shape as the existing `created_at` / `last_seen_at`).
--     Co-populated with `permission_decided_by` on each decision.
--
-- Grandfathered-row invariant:
--
--   Every row that exists at migration time has
--   `permission_state='granted'` and `permission_decided_by=NULL`
--   (the column default + NULL preservation). v0.8.0's auth gate
--   reads `permission_state='granted'` as the admission check, so
--   no existing user is locked out by the upgrade. v0.7.0's OTC
--   path is patched in the same release to set
--   `permission_state='pending'` explicitly on a fresh row, so the
--   gate engages only for users provisioned after the upgrade.

ALTER TABLE users ADD COLUMN permission_state TEXT NOT NULL DEFAULT 'granted'
    CHECK (permission_state IN ('pending', 'granted', 'revoked'));

ALTER TABLE users ADD COLUMN first_name TEXT;
ALTER TABLE users ADD COLUMN last_name TEXT;
ALTER TABLE users ADD COLUMN beta_request_reason TEXT;

ALTER TABLE users ADD COLUMN permission_decided_by INTEGER
    REFERENCES users(id) ON DELETE SET NULL;
ALTER TABLE users ADD COLUMN permission_decided_at TEXT;

-- Index for the v0.9.0 admin queue: list pending requests ordered by
-- when the user's row was created (the implicit "request received at"
-- timestamp, since v0.8.0 sets pending at the same moment as the row
-- itself is inserted via the OTC verify path).
CREATE INDEX idx_users_permission_state ON users (permission_state);
