// ============================================================
// AI ENDPOINT CONTRACT TESTS — hit the REAL server handlers
// ============================================================
// The UI smoke tests mock AI responses via page.route() so they never
// exercise the server-side handler. That meant a route whose require()
// statement was wrong (undefined PROMPTS → 500) shipped to prod without
// any test failing. This spec calls each AI generation endpoint
// through the Playwright request fixture (bypasses page.route) with a
// minimal valid payload and only checks the server didn't crash with a
// ReferenceError / TypeError. A mock model is installed on the server
// side via MOCK_AI=1 env var (if configured) so we don't spend API
// credits; otherwise the server still processes the request and returns
// a structured error (which is fine — we're guarding against 500s from
// bad imports, not end-to-end AI generation).
//
// A non-500 response (200 OK or 4xx with structured JSON) means the
// handler at least ran — that's the contract we're verifying.
// ============================================================

const { test, expect, E2E_BASE, getAuthToken } = require('../fixtures');

test.describe('AI endpoint contracts — handler loads + accepts POST', () => {
  let token;

  test.beforeAll(async ({ request }) => {
    token = await getAuthToken(request);
  });

  // Each entry: path, minimal body that should make the handler run past
  // its import statements. We don't need a valid AI key — a 500 from
  // a require() bug will still fail, but a 4xx from "missing API key"
  // is acceptable because it proves the route loaded.
  const endpoints = [
    {
      path: '/api/generate-pe-narrative',
      body: {
        steps: [{ component: 'Inspection', label: 'General', method: 'Observed', status: 'normal' }],
        ageGroup: 'School-Age (6-11 yr)',
        system: 'neuro',
        patientAge: '8 years',
        patientGender: 'male',
        format: 'narrative',
      },
    },
    {
      path: '/api/generate-milestone-narrative',
      body: {
        milestones: [{ domain: 'Motor', label: 'Walks', status: 'achieved' }],
        ageGroup: '12 months',
        patientAge: '12 months',
        patientGender: 'male',
      },
    },
    {
      path: '/api/generate-hpi-encounter',
      body: { transcript: 'Patient with cough for 3 days.', setting: 'outpatient' },
    },
    {
      path: '/api/sick-visit/note',
      body: { chiefComplaint: 'Cough', transcript: 'Cough x 3 days.' },
    },
    {
      path: '/api/generate-soap',
      body: { transcript: 'Patient with cough x 3 days.' },
    },
    {
      path: '/api/generate-chart-review',
      body: { pmh: 'None', outpatientVisits: [], edVisits: [], subspecialtyVisits: [], labs: [] },
    },
    {
      path: '/api/refine',
      body: { currentDocument: 'MOCK doc', instructions: 'Add severity.' },
    },
    {
      path: '/api/well-visit/shadess',
      body: { answers: { home: 'lives with parents' } },
    },
  ];

  // Signatures of runtime bugs that mean the handler crashed before it
  // could reach its try/catch (i.e. the exact class of bug being guarded).
  const CRASH_SIGNATURES = [
    /Cannot read properties of undefined/i,
    /is not a function/i,
    /is not defined/i,
    /ReferenceError/i,
    /TypeError/i,
  ];

  for (const { path, body } of endpoints) {
    test(`${path} — handler loads, returns structured JSON, no import-bug crash`, async ({ request }) => {
      const r = await request.post(E2E_BASE + path, {
        headers: { Authorization: 'Bearer ' + token },
        data: body,
      });
      // Must always be JSON — a 500 HTML page means the express error handler
      // caught an unhandled exception (our bug class).
      let json = null;
      try { json = await r.json(); } catch (_) { /* remains null */ }
      expect(json, `${path} did not return JSON (HTTP ${r.status()})`).toBeTruthy();

      // If the response leaked a JS error message through to the client,
      // that's the import/destructure-bug signature we want to catch.
      const errText = (json && (json.error || json.message)) || '';
      for (const sig of CRASH_SIGNATURES) {
        expect(errText, `${path} leaked a runtime error: ${errText}`).not.toMatch(sig);
      }
      // Errors should be plain strings — never raw error objects.
      if (json && json.success === false) {
        expect(typeof json.error).toBe('string');
      }
    });
  }
});
