-- PostgreSQL schema for Biz Connect — the target of the SQLite→Postgres migration.
--
-- Every column is defined up front here (unlike the SQLite db.js, which layers columns via ALTER TABLE
-- and hit a real ordering bug). Type mapping from the SQLite schema:
--   SQLite INTEGER epoch-ms timestamp  -> BIGINT     (ms since epoch; JS Number-safe)
--   SQLite INTEGER 0/1 boolean flag    -> SMALLINT   (kept numeric so app code still reads 0/1)
--   SQLite INTEGER byte size / duration-> BIGINT     (files can exceed INT range)
--   SQLite INTEGER small count (peak)  -> INTEGER
--   SQLite AUTOINCREMENT rowid         -> BIGINT GENERATED ALWAYS AS IDENTITY
--   TEXT                               -> TEXT
-- Foreign keys mirror the three the SQLite schema enforced (users→teams, sessions_auth→users,
-- machines→teams). The data-migration script inserts in dependency order so these hold.

CREATE TABLE IF NOT EXISTS teams (
  id          TEXT PRIMARY KEY,
  name        TEXT NOT NULL,
  created_at  BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS users (
  id               TEXT PRIMARY KEY,
  team_id          TEXT NOT NULL REFERENCES teams(id),
  email            TEXT NOT NULL UNIQUE,
  pw_hash          TEXT NOT NULL,
  pw_salt          TEXT NOT NULL,
  role             TEXT NOT NULL DEFAULT 'technician',
  mfa_secret       TEXT,
  mfa_enabled      SMALLINT NOT NULL DEFAULT 0,
  created_at       BIGINT NOT NULL,
  name             TEXT,
  active           SMALLINT NOT NULL DEFAULT 1,
  avatar_url       TEXT,
  status           TEXT NOT NULL DEFAULT 'active',
  bizgaze_user_id  TEXT,
  last_seen        BIGINT
);
CREATE INDEX IF NOT EXISTS idx_users_bizgaze_user_id ON users(bizgaze_user_id);

CREATE TABLE IF NOT EXISTS sessions_auth (
  token       TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL REFERENCES users(id),
  mfa_passed  SMALLINT NOT NULL DEFAULT 0,
  created_at  BIGINT NOT NULL,
  expires_at  BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS machines (
  id            TEXT PRIMARY KEY,
  team_id       TEXT NOT NULL REFERENCES teams(id),
  name          TEXT NOT NULL,
  enroll_token  TEXT NOT NULL UNIQUE,
  unattended    SMALLINT NOT NULL DEFAULT 0,
  last_seen     BIGINT,
  created_at    BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS audit_log (
  id            BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
  team_id       TEXT NOT NULL,
  user_id       TEXT,
  user_email    TEXT,
  machine_id    TEXT,
  machine_name  TEXT,
  action        TEXT NOT NULL,
  detail        TEXT,
  at            BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS sessions_log (
  id          TEXT PRIMARY KEY,
  team_id     TEXT NOT NULL,
  agent_email TEXT,
  agent_name  TEXT,
  ticket      TEXT,
  started_at  BIGINT NOT NULL,
  ended_at    BIGINT,
  recording   TEXT,
  transcript  TEXT
);

CREATE TABLE IF NOT EXISTS refresh_tokens (
  token_hash  TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  created_at  BIGINT NOT NULL,
  expires_at  BIGINT NOT NULL,
  revoked     SMALLINT NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS api_keys (
  id            TEXT PRIMARY KEY,
  team_id       TEXT NOT NULL,
  name          TEXT,
  key_hash      TEXT NOT NULL UNIQUE,
  scopes        TEXT NOT NULL DEFAULT '',
  created_by    TEXT,
  created_at    BIGINT NOT NULL,
  last_used_at  BIGINT,
  revoked       SMALLINT NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS webhooks (
  id           TEXT PRIMARY KEY,
  team_id      TEXT NOT NULL,
  url          TEXT NOT NULL,
  secret       TEXT NOT NULL,
  events       TEXT NOT NULL DEFAULT '',
  active       SMALLINT NOT NULL DEFAULT 1,
  created_by   TEXT,
  created_at   BIGINT NOT NULL,
  last_status  INTEGER,
  last_error   TEXT,
  last_at      BIGINT
);

CREATE TABLE IF NOT EXISTS messages (
  id              TEXT PRIMARY KEY,
  team_id         TEXT NOT NULL,
  sender_id       TEXT NOT NULL,
  recipient_id    TEXT NOT NULL,
  body            TEXT NOT NULL,
  created_at      BIGINT NOT NULL,
  read_at         BIGINT,
  reply_to        TEXT,
  attachment_id   TEXT,
  conversation_id TEXT,
  mentions        TEXT,
  delivered_at    BIGINT,
  poll_id         TEXT,
  msg_type        TEXT,
  deleted         SMALLINT NOT NULL DEFAULT 0,
  edited_at       BIGINT,
  fwd_from        TEXT
);
CREATE INDEX IF NOT EXISTS idx_messages_pair   ON messages(team_id, sender_id, recipient_id, created_at);
CREATE INDEX IF NOT EXISTS idx_messages_unread ON messages(team_id, recipient_id, sender_id, read_at);
CREATE INDEX IF NOT EXISTS idx_messages_conv   ON messages(conversation_id, created_at);
-- New: attachment lookups drove the /files auth scan (see static.js authAttachment). Index it so the
-- per-Range playback auth is a keyed lookup, not a table scan (the auth cache stays as a second line).
CREATE INDEX IF NOT EXISTS idx_messages_attachment ON messages(attachment_id);

CREATE TABLE IF NOT EXISTS message_reactions (
  message_id  TEXT NOT NULL,
  user_id     TEXT NOT NULL,
  emoji       TEXT NOT NULL,
  created_at  BIGINT NOT NULL,
  PRIMARY KEY (message_id, user_id, emoji)
);

CREATE TABLE IF NOT EXISTS attachments (
  id           TEXT PRIMARY KEY,
  team_id      TEXT NOT NULL,
  uploader_id  TEXT NOT NULL,
  name         TEXT NOT NULL,
  mime         TEXT,
  size         BIGINT,
  created_at   BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS conversations (
  id          TEXT PRIMARY KEY,
  team_id     TEXT NOT NULL,
  type        TEXT NOT NULL DEFAULT 'group',
  name        TEXT,
  created_by  TEXT,
  created_at  BIGINT NOT NULL,
  avatar_id   TEXT,
  admin_only  SMALLINT NOT NULL DEFAULT 0
);

CREATE TABLE IF NOT EXISTS conversation_members (
  conversation_id  TEXT NOT NULL,
  user_id          TEXT NOT NULL,
  last_read_at     BIGINT NOT NULL DEFAULT 0,
  joined_at        BIGINT NOT NULL,
  admin            SMALLINT NOT NULL DEFAULT 0,
  PRIMARY KEY (conversation_id, user_id)
);

CREATE TABLE IF NOT EXISTS call_history (
  id           TEXT PRIMARY KEY,
  team_id      TEXT NOT NULL,
  room         TEXT NOT NULL,
  group_id     TEXT,
  kind         TEXT,
  title        TEXT,
  peak         INTEGER NOT NULL DEFAULT 0,
  participants TEXT,
  uids         TEXT,
  started_at   BIGINT NOT NULL,
  ended_at     BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_call_history_team ON call_history(team_id, ended_at);

CREATE TABLE IF NOT EXISTS user_aliases (
  old_id      TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  team_id     TEXT,
  created_at  BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS polls (
  id               TEXT PRIMARY KEY,
  team_id          TEXT NOT NULL,
  conversation_id  TEXT NOT NULL,
  message_id       TEXT,
  question         TEXT NOT NULL,
  options          TEXT NOT NULL,
  multi            SMALLINT NOT NULL DEFAULT 0,
  closed           SMALLINT NOT NULL DEFAULT 0,
  created_by       TEXT NOT NULL,
  created_at       BIGINT NOT NULL
);

CREATE TABLE IF NOT EXISTS poll_votes (
  poll_id     TEXT NOT NULL,
  user_id     TEXT NOT NULL,
  option_idx  INTEGER NOT NULL,
  created_at  BIGINT NOT NULL,
  PRIMARY KEY (poll_id, user_id, option_idx)
);

CREATE TABLE IF NOT EXISTS scheduled_meetings (
  id             TEXT PRIMARY KEY,
  team_id        TEXT NOT NULL,
  group_id       TEXT,
  room_code      TEXT NOT NULL,
  title          TEXT NOT NULL,
  description    TEXT,
  scheduled_at   BIGINT NOT NULL,
  created_by     TEXT NOT NULL,
  created_at     BIGINT NOT NULL,
  ended_at       BIGINT,
  participants   TEXT,
  reminded       SMALLINT NOT NULL DEFAULT 0,
  cancelled      SMALLINT NOT NULL DEFAULT 0,
  duration_mins  INTEGER,
  recurrence     TEXT,
  guest_emails   TEXT,
  lobby          SMALLINT
);
CREATE INDEX IF NOT EXISTS idx_sched_team ON scheduled_meetings(team_id, scheduled_at);
CREATE INDEX IF NOT EXISTS idx_sched_code ON scheduled_meetings(room_code);

CREATE TABLE IF NOT EXISTS recordings (
  id               TEXT PRIMARY KEY,
  team_id          TEXT NOT NULL,
  room             TEXT,
  group_id         TEXT,
  meeting_id       TEXT,
  title            TEXT,
  kind             TEXT NOT NULL,
  file             TEXT,
  mime             TEXT,
  size             BIGINT,
  duration_ms      BIGINT,
  created_by       TEXT,
  created_by_name  TEXT,
  created_at       BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_rec_team ON recordings(team_id, created_at);
CREATE INDEX IF NOT EXISTS idx_rec_room ON recordings(room);

CREATE TABLE IF NOT EXISTS push_subscriptions (
  id          TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  endpoint    TEXT NOT NULL UNIQUE,
  p256dh      TEXT NOT NULL,
  auth        TEXT NOT NULL,
  created_at  BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_push_user ON push_subscriptions(user_id);

CREATE TABLE IF NOT EXISTS device_tokens (
  id          TEXT PRIMARY KEY,
  user_id     TEXT NOT NULL,
  tenant_id   TEXT,
  platform    TEXT NOT NULL,
  token       TEXT NOT NULL UNIQUE,
  created_at  BIGINT NOT NULL,
  last_seen   BIGINT
);
CREATE INDEX IF NOT EXISTS idx_devtok_user ON device_tokens(user_id);

CREATE TABLE IF NOT EXISTS app_installs (
  id           TEXT PRIMARY KEY,
  install_id   TEXT NOT NULL UNIQUE,
  user_id      TEXT,
  user_email   TEXT,
  tenant_id    TEXT,
  platform     TEXT,
  app_version  TEXT,
  os           TEXT,
  first_seen   BIGINT NOT NULL,
  last_seen    BIGINT NOT NULL
);
CREATE INDEX IF NOT EXISTS idx_installs_tenant ON app_installs(tenant_id);
CREATE INDEX IF NOT EXISTS idx_installs_user   ON app_installs(user_id);

CREATE TABLE IF NOT EXISTS favorites (
  user_id     TEXT NOT NULL,
  target      TEXT NOT NULL,
  created_at  BIGINT NOT NULL,
  PRIMARY KEY (user_id, target)
);
