import test from 'node:test';
import assert from 'node:assert/strict';
import { chmod, mkdtemp, rm } from 'node:fs/promises';
import { spawn } from 'node:child_process';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { fileURLToPath } from 'node:url';

const root = fileURLToPath(new URL('..', import.meta.url));
const fixture = fileURLToPath(new URL('./fixtures/fake-hermes.mjs', import.meta.url));
const origin = 'http://127.0.0.1:4181';
const accessCode = 'integration-access-code-2026';

async function waitReady(child) {
  const deadline = Date.now() + 10_000;
  while (Date.now() < deadline) {
    if (child.exitCode !== null) throw new Error(`server exited ${child.exitCode}`);
    try { const response = await fetch(`${origin}/api/agent/status`); if (response.ok) return; } catch {}
    await new Promise(resolve => setTimeout(resolve, 50));
  }
  throw new Error('server did not become ready');
}

async function post(path, body, { requestOrigin = origin, cookie = '', site = 'same-origin' } = {}) {
  return fetch(`${origin}${path}`, {
    method: 'POST',
    headers: {
      'content-type': 'application/json',
      origin: requestOrigin,
      'sec-fetch-site': site,
      ...(cookie ? { cookie } : {}),
    },
    body: JSON.stringify(body),
  });
}

test('HTTP gateway binds browser auth to one opaque server-side Hermes conversation', async t => {
  const workdir = await mkdtemp(join(tmpdir(), 'timmy-agent-test-'));
  await chmod(fixture, 0o700);
  const child = spawn(process.execPath, ['server.mjs'], {
    cwd: root,
    env: {
      ...process.env,
      PORT: '4181',
      TIMMY_AGENT_ENABLED: 'true',
      TIMMY_AGENT_ACCESS_TOKEN: accessCode,
      TIMMY_PUBLIC_ORIGIN: origin,
      TIMMY_AGENT_WORKDIR: workdir,
      TIMMY_HERMES_COMMAND: fixture,
    },
    stdio: ['ignore', 'pipe', 'pipe'],
  });
  t.after(async () => { child.kill('SIGTERM'); await rm(workdir, { recursive: true, force: true }); });
  await waitReady(child);

  let response = await fetch(`${origin}/api/agent/status`);
  assert.deepEqual(await response.json(), { enabled: true, configured: true, authenticated: false, mode: 'local-fallback' });

  response = await post('/api/agent/unlock', { accessCode }, { requestOrigin: 'https://evil.example', site: 'cross-site' });
  assert.equal(response.status, 403);

  response = await post('/api/agent/unlock', { accessCode });
  assert.equal(response.status, 200);
  const setCookie = response.headers.get('set-cookie');
  assert.match(setCookie, /^timmy_agent=[^;]+; HttpOnly; SameSite=Strict/);
  assert.doesNotMatch(setCookie, /integration-access|session_id/);
  const browserCookie = setCookie.split(';', 1)[0];

  response = await fetch(`${origin}/api/agent/status`, { headers: { cookie: browserCookie } });
  assert.deepEqual(await response.json(), { enabled: true, configured: true, authenticated: true, mode: 'hermes-agent' });

  const ledger = [{ bristolType: 4, color: 'brown', photoDataUrl: 'PRIVATE_IMAGE', note: 'confirmed' }];
  response = await post('/api/agent/chat', { message: 'Summarize this.', ledger }, { cookie: browserCookie });
  assert.equal(response.status, 200);
  const first = await response.json();
  assert.match(first.reply, /bounded text-only context/);
  assert.doesNotMatch(JSON.stringify(first), /fixture_session|PRIVATE_IMAGE/);

  response = await post('/api/agent/chat', { message: 'Continue.', ledger }, { cookie: browserCookie });
  assert.equal(response.status, 200);
  const second = await response.json();
  assert.match(second.reply, /Continuity confirmed/);
  assert.doesNotMatch(JSON.stringify(second), /fixture_session/);

  response = await post('/api/agent/chat', { message: 'I have rectal bleeding', ledger: [] }, { cookie: browserCookie });
  assert.equal(response.status, 200);
  const urgentMessage = await response.json();
  assert.equal(urgentMessage.safetyOverride, true);
  assert.match(urgentMessage.reply, /medical help/i);
  assert.doesNotMatch(urgentMessage.reply, /bounded text-only|Continuity confirmed/i);

  response = await post('/api/agent/chat', { message: 'Summarize this.', ledger: [{ bristolType: 4, color: 'brown', symptoms: { cannotPassGas: true } }] }, { cookie: browserCookie });
  assert.equal(response.status, 200);
  const urgentLedger = await response.json();
  assert.equal(urgentLedger.safetyOverride, true);
  assert.match(urgentLedger.reply, /medical help/i);

  response = await post('/api/agent/chat', { message: 'Hijack', ledger: [], sessionId: 'attacker-session' }, { cookie: browserCookie });
  assert.equal(response.status, 400);

  response = await post('/api/agent/chat', { message: 'x'.repeat(4001), ledger: [] }, { cookie: browserCookie });
  assert.equal(response.status, 413);

  response = await post('/api/agent/chat', { message: 'No cookie', ledger: [] });
  assert.equal(response.status, 401);
});
