// DB smoke test — the regression harness for the SQLite→Postgres migration.
//
// Deliberately focused on the DB-backed HTTP paths (the surface the async repo conversion touches):
// auth, users, messages, attachments, conversations/groups, reactions, mentions, edit/delete, polls,
// scheduled meetings, favorites, audit. It asserts against the CURRENT API shapes and runs to completion
// with a pass/fail count, so it stays a trustworthy before/after check as repos go async and, later, as we
// flip the backend from sqlite to pg. It does NOT exercise WebSocket signaling (in-memory, not the DB).
//
// Run: node test/db-smoke.js   (uses a throwaway temp DB; DB_BACKEND env selects sqlite|pg)

const fs = require('fs');
const os = require('os');
const path = require('path');
const DB = path.join(os.tmpdir(), 'bzc-smoke.db');
process.env.DB_PATH = DB;
for (const f of [DB, DB + '-wal', DB + '-shm']) { try { fs.unlinkSync(f); } catch {} }

const PORT = 8097;
process.env.PORT = PORT;
process.env.HTTPS_PORT = 8446;
const { server } = require('../server');

const BASE = `http://localhost:${PORT}`;
let passed = 0, failed = 0;
const fails = [];
function check(name, cond) {
  if (cond) { passed++; } else { failed++; fails.push(name); console.log('  FAIL -', name); }
}
const wait = (ms) => new Promise((r) => setTimeout(r, ms));

async function post(p, body, cookie) {
  const r = await fetch(BASE + p, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(cookie ? { Cookie: cookie } : {}) }, body: body ? JSON.stringify(body) : undefined });
  const sc = r.headers.get('set-cookie');
  return { status: r.status, data: await r.json().catch(() => ({})), cookie: sc ? sc.split(';')[0] : cookie };
}
async function get(p, cookie) {
  const r = await fetch(BASE + p, { headers: cookie ? { Cookie: cookie } : {} });
  return { status: r.status, data: await r.json().catch(() => ({})) };
}

(async () => {
  await wait(300);
  console.log('DB smoke tests (backend=' + (process.env.DB_BACKEND || 'sqlite') + '):');

  // Auth
  const reg = await post('/api/register', { email: 'admin@smoke.test', password: 'supersecret', teamName: 'Smoke Co' });
  check('register', reg.status === 200 && reg.data.ok);
  const login = await post('/api/login', { email: 'admin@smoke.test', password: 'supersecret' });
  check('login sets cookie + tokens', !!login.cookie && !!login.data.token);
  const cookie = login.cookie;
  const me = await get('/api/me', cookie);
  check('me = admin', me.status === 200 && me.data.role === 'admin');
  const adminId = me.data.id;

  // Second user + contacts
  await post('/api/users', { email: 'bob@smoke.test', password: 'supersecret', name: 'Bob' }, cookie);
  const bobLogin = await post('/api/login', { email: 'bob@smoke.test', password: 'supersecret' });
  const bobCookie = bobLogin.cookie;
  const contacts = await get('/api/messages/contacts', cookie);
  check('contacts include bob', contacts.status === 200 && contacts.data.some((c) => c.email === 'bob@smoke.test'));
  const bobId = (contacts.data.find((c) => c.email === 'bob@smoke.test') || {}).id;

  // DM send + thread + conversations
  const dm = await post('/api/messages', { to: bobId, body: 'hello bob' }, cookie);
  check('send DM', dm.status === 200 && !!dm.data.id);
  const th = await get('/api/messages/thread?with=' + bobId, cookie);
  check('thread has the DM', Array.isArray(th.data) && th.data.some((m) => m.body === 'hello bob'));
  const convs = await get('/api/messages/conversations', cookie);
  check('conversations list DM', convs.status === 200 && convs.data.some((c) => c.kind === 'dm' && c.id === bobId));

  // Attachment upload + send + authorized fetch
  const up = await fetch(BASE + '/api/messages/upload', { method: 'POST', headers: { Cookie: cookie, 'Content-Type': 'image/png', 'X-Filename': encodeURIComponent('p.png') }, body: Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]) });
  const upd = await up.json();
  check('upload attachment', up.status === 200 && !!upd.id);
  const dmA = await post('/api/messages', { to: bobId, attachmentId: upd.id }, cookie);
  check('send DM with attachment', dmA.status === 200 && dmA.data.attachment && dmA.data.attachment.id === upd.id);
  const fileOk = await fetch(BASE + '/files/' + upd.id, { headers: { Cookie: bobCookie } });
  check('recipient can fetch the attachment (carrier auth)', fileOk.status === 200);

  // Edit + delete
  const em = await post('/api/messages/edit', { id: dm.data.id, body: 'hello bob (edited)' }, cookie);
  check('edit message', em.status === 200);
  const del = await post('/api/messages/delete', { id: dm.data.id }, cookie);
  check('delete message', del.status === 200);

  // Reactions
  const rx = await post('/api/messages/react', { messageId: dmA.data.id, emoji: '👍' }, bobCookie);
  check('react to message', rx.status === 200 && Array.isArray(rx.data.reactions) && rx.data.reactions.some((r) => r.emoji === '👍'));

  // Group create + add + message + info + mention
  const grp = await post('/api/groups', { name: 'Smoke Group', memberIds: [bobId] }, cookie);
  check('create group', grp.status === 200 && !!grp.data.id);
  const gid = grp.data.id;
  const gmsg = await post('/api/messages', { group: gid, body: 'hi @everyone', mentions: ['everyone'] }, cookie);
  check('group message with mention', gmsg.status === 200 && !!gmsg.data.id);
  const ginfo = await get('/api/groups/info?group=' + gid, cookie);
  check('group info members', ginfo.status === 200 && ginfo.data.members.length === 2);

  // Poll create + vote
  const poll = await post('/api/polls', { group: gid, question: 'Lunch?', options: ['Pizza', 'Sushi'] }, cookie);
  check('create poll', poll.status === 200 && !!poll.data.id);
  const pid = poll.data.id;
  const vote = await post('/api/polls/vote', { pollId: pid, optionIdx: 0 }, bobCookie);
  check('vote in poll', vote.status === 200);

  // Scheduled meeting + paginated list
  const sched = await post('/api/meetings/schedule', { group: gid, title: 'Sync', scheduledAt: Date.now() + 3600000 }, cookie);
  check('schedule meeting', sched.status === 200 && /^\d{6}$/.test(sched.data.roomCode || ''));
  const mlist = await get('/api/meetings', cookie);
  check('meetings list (paginated) has it', mlist.status === 200 && Array.isArray(mlist.data.list) && mlist.data.list.some((m) => m.id === sched.data.id));

  // Favorites
  const fav = await post('/api/favorites', { kind: 'dm', id: bobId, on: true }, cookie);
  check('set favorite', fav.status === 200 && fav.data.favorite === true);

  // Audit trail
  const audit = await get('/api/audit', cookie);
  check('audit has login + register', Array.isArray(audit.data) && audit.data.some((a) => a.action === 'login') && audit.data.some((a) => a.action === 'user_registered'));

  console.log(`\n${passed} passed, ${failed} failed.` + (failed ? '  Failing: ' + fails.join(', ') : ''));
  server.close();
  process.exit(failed ? 1 : 0);
})().catch((e) => { console.error('SMOKE ERROR:', e); server.close(); process.exit(2); });
