-- T2 — control-plane schema. Transcribes INDEX §4.2 (accounts/hosts/sessions/pairing_codes)
-- VERBATIM, plus P3-owned tables (relay_nodes, metering_samples, audit_log) and the INV8 companion
-- status-version tables + current-pointer columns (OQ6 — pending INDEX promotion of §4.2).
--
-- Discipline: no bare UPDATE of a lifecycle/business field without a matching *_status_versions
-- insert in the SAME transaction. Liveness columns (last_seen, last_attach_at) are advisory
-- caches (INV7 — live truth is Redis) and are exempt from versioning.

CREATE EXTENSION IF NOT EXISTS "pgcrypto"; -- gen_random_uuid()

-- ---- accounts (§4.2) --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS accounts (
  account_id     uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- unguessable, never recycled
  plan           text NOT NULL,                              -- 'free'|'personal'|'pro'|'team'
  created_at     timestamptz NOT NULL DEFAULT now(),
  status         text NOT NULL DEFAULT 'active',             -- 'active'|'suspended' (current pointer)
  status_version int  NOT NULL DEFAULT 1,                    -- INV8 monotonic pointer
  CONSTRAINT accounts_plan_chk   CHECK (plan   IN ('free','personal','pro','team')),
  CONSTRAINT accounts_status_chk CHECK (status IN ('active','suspended'))
);

CREATE TABLE IF NOT EXISTS account_status_versions (
  account_id uuid NOT NULL REFERENCES accounts(account_id),
  version    int  NOT NULL,
  status     text NOT NULL,
  changed_at timestamptz NOT NULL DEFAULT now(),
  changed_by text NOT NULL,
  PRIMARY KEY (account_id, version)
);

-- ---- hosts (§4.2) — ownership source of truth --------------------------------------------------
CREATE TABLE IF NOT EXISTS hosts (
  host_id        uuid PRIMARY KEY DEFAULT gen_random_uuid(), -- unguessable, NEVER recycled (INV1)
  account_id     uuid NOT NULL REFERENCES accounts(account_id),
  subdomain      text NOT NULL UNIQUE,                       -- stable across reconnects
  agent_pubkey   bytea NOT NULL,                             -- Ed25519 PUBLIC key only (INV4)
  enroll_fpr     text NOT NULL,                              -- fingerprint of agent_pubkey (E2E TOFU)
  status         text NOT NULL DEFAULT 'offline',            -- 'online'|'offline'|'draining'|'revoked'
  last_seen      timestamptz NOT NULL DEFAULT now(),         -- advisory cache (NOT versioned, INV7)
  created_at     timestamptz NOT NULL DEFAULT now(),
  revoked_at     timestamptz NULL,                           -- set on revocation (INV12)
  status_version int NOT NULL DEFAULT 1,
  CONSTRAINT hosts_status_chk CHECK (status IN ('online','offline','draining','revoked'))
);
CREATE INDEX IF NOT EXISTS hosts_account_idx ON hosts(account_id);

CREATE TABLE IF NOT EXISTS host_status_versions (
  host_id    uuid NOT NULL REFERENCES hosts(host_id),
  version    int  NOT NULL,
  status     text NOT NULL,
  revoked_at timestamptz NULL,
  changed_at timestamptz NOT NULL DEFAULT now(),
  changed_by text NOT NULL,
  PRIMARY KEY (host_id, version)
);

-- ---- sessions (§4.2) --------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS sessions (
  session_id     uuid PRIMARY KEY,                           -- base-app sessionId, opaque to relay
  host_id        uuid NOT NULL REFERENCES hosts(host_id),
  account_id     uuid NOT NULL,                              -- DENORMALIZED for O(1) authz (INV3/INV6)
  created_at     timestamptz NOT NULL DEFAULT now(),
  last_attach_at timestamptz NOT NULL DEFAULT now()          -- advisory cache (NOT versioned)
);

-- ---- pairing_codes (§4.2/§4.5) ----------------------------------------------------------------
CREATE TABLE IF NOT EXISTS pairing_codes (
  code_hash       text PRIMARY KEY,                          -- hash of single-use code; raw NEVER stored (INV5)
  account_id      uuid NOT NULL REFERENCES accounts(account_id),
  expires_at      timestamptz NOT NULL,                      -- short TTL
  redeemed_at     timestamptz NULL,                          -- single-use: non-null ⇒ spent
  redeem_attempts int NOT NULL DEFAULT 0                     -- code-scoped brute-force lockout (T8)
);

-- ---- relay_nodes (P3-owned) -------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS relay_nodes (
  node_id   text PRIMARY KEY,                                -- SPIFFE-style id from mTLS cert subject
  addr      text NOT NULL,
  status    text NOT NULL DEFAULT 'active',                  -- 'active'|'draining'
  last_seen timestamptz NOT NULL DEFAULT now(),
  CONSTRAINT relay_nodes_status_chk CHECK (status IN ('active','draining'))
);

-- ---- metering_samples (P3-owned, append-only INV8) --------------------------------------------
CREATE TABLE IF NOT EXISTS metering_samples (
  id                 bigserial PRIMARY KEY,
  host_id            uuid NOT NULL REFERENCES hosts(host_id),
  account_id         uuid NOT NULL,                          -- derived server-side from ownership (INV1)
  node_id            text NOT NULL,                          -- authenticated mTLS identity (INV3-analog)
  concurrent_viewers int NOT NULL,
  sampled_at         timestamptz NOT NULL
);
CREATE INDEX IF NOT EXISTS metering_account_time_idx ON metering_samples(account_id, sampled_at);

-- ---- audit_log (P3-owned, append-only zero-payload INV10) -------------------------------------
CREATE TABLE IF NOT EXISTS audit_log (
  id           bigserial PRIMARY KEY,
  action       text NOT NULL,
  principal_id text NOT NULL,
  account_id   text NOT NULL,
  host_id      text NULL,
  ts           timestamptz NOT NULL DEFAULT now(),
  meta         jsonb NOT NULL DEFAULT '{}'::jsonb            -- metadata only; NO terminal payload (INV10)
);
CREATE INDEX IF NOT EXISTS audit_account_time_idx ON audit_log(account_id, ts);
