// P9 slice-4 observability smoke: the mandated "trace id appears in DB, event and
// logs" gate, end to end. (a) a response carries x-trace-id; (b) an action taken with
// a chosen x-trace-id emits a CloudEvent whose correlationId == that trace (event);
// (c) an IiosAuditLink row carries the trace (DB); (d) /metrics returns tenant counters.
// Requires the service running with IIOS_DEV_TOKENS=1.
import 'dotenv/config';

const SERVICE = process.env.SMOKE_URL ?? 'http://localhost:3200';
const APP_ID = 'portal-demo';
const assert = (c, m) => { if (!c) { console.error('✗', m); process.exit(1); } console.log('✓', m); };
const START = '2026-07-02T10:00:00.000Z';

async function devToken(userId) {
  const r = await fetch(`${SERVICE}/v1/dev/token`, {
    method: 'POST', headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ appId: APP_ID, userId, name: userId, orgId: 'org_obs' }),
  });
  if (!r.ok) throw new Error(`devToken ${r.status}`);
  return (await r.json()).token;
}
const TOKEN = await devToken('obs-admin');
const TRACE = `obs-trace-${Date.now()}`;

async function call(path, method = 'GET', body, extraHeaders = {}) {
  return fetch(`${SERVICE}${path}`, {
    method,
    headers: { 'content-type': 'application/json', authorization: `Bearer ${TOKEN}`, ...extraHeaders },
    body: body ? JSON.stringify(body) : undefined,
  });
}
const json = async (...a) => { const r = await call(...a); if (!r.ok) throw new Error(`${a[1] ?? 'GET'} ${a[0]} ${r.status}: ${await r.text()}`); return r.json(); };

// (a) response carries x-trace-id
const health = await fetch(`${SERVICE}/health`);
assert(!!health.headers.get('x-trace-id'), 'response carries an x-trace-id header (trace in logs/headers)');

// (b)+(c) schedule a meeting then summarize under our chosen trace id → event correlationId + audit link carry it
const meeting = await json('/v1/calendar/meetings', 'POST', { meetingType: 'INTERNAL', title: 'obs meeting', startAt: START }, { 'x-trace-id': TRACE });
// grant consent for the single organizer participant, generate transcript, summarize
for (const p of meeting.participants) if (p.actorRefId) await call(`/v1/calendar/meetings/${meeting.id}/consent`, 'POST', { actorRefId: p.actorRefId, status: 'GRANTED' });
await call(`/v1/calendar/meetings/${meeting.id}/transcript`, 'POST');
const sumRes = await call(`/v1/calendar/meetings/${meeting.id}/summarize`, 'POST', undefined, { 'x-trace-id': TRACE });
assert(sumRes.headers.get('x-trace-id') === TRACE, 'x-trace-id echoed back on the summarize request');

// (d) /metrics returns tenant counters (+ audit link count proving the trace hit the DB)
const metrics = await json('/metrics');
assert(metrics.scope?.orgId === 'org_obs' && metrics.scope?.cellId, '/metrics is caller-tenant scoped (org + cell)');
assert(Array.isArray(metrics.tenant?.meetings) && metrics.tenant.meetings.length >= 1, '/metrics reports the tenant meeting counter');
assert(metrics.tenant.auditLinks >= 1, '/metrics reports audit links (trace bound in DB)');
assert(typeof metrics.relay?.outboxPending === 'number', '/metrics reports global relay lag');

console.log('\nP9 observability smoke: PASS');
process.exit(0);
