import test from 'node:test';
import assert from 'node:assert/strict';

import {
  buildVisionRequest,
  mergeVisualSuggestion,
  parseVisionResponse,
  validatePhotoPayload,
} from '../src/analysis.js';

test('validates a confident visual suggestion without inventing nonvisual fields', () => {
  const result = parseVisionResponse({
    isStool: true,
    bristolType: 4,
    color: 'brown',
    confidence: 0.82,
    imageQuality: 'good',
    observations: 'Smooth, formed appearance.',
    urgency: 4,
    discomfort: 3,
    symptoms: { blood: true },
    diagnosis: 'anything',
  });
  assert.deepEqual(result, {
    status: 'suggestion',
    isStool: true,
    bristolType: 4,
    color: 'brown',
    confidence: 0.82,
    imageQuality: 'good',
    observations: 'Smooth, formed appearance.',
    warning: 'Visual suggestion only. Confirm it yourself; this is not a diagnosis.',
  });
  assert.equal(result.urgency, undefined);
  assert.equal(result.symptoms, undefined);
  assert.equal(result.diagnosis, undefined);
});

test('fails closed on a non-stool or low-confidence image', () => {
  assert.deepEqual(parseVisionResponse({ isStool: false, confidence: 0.9 }), {
    status: 'needs_user_input',
    isStool: false,
    reason: 'The image does not clearly show stool.',
  });
  assert.deepEqual(parseVisionResponse({ isStool: true, bristolType: 4, color: 'brown', confidence: 0.31 }), {
    status: 'needs_user_input',
    isStool: true,
    reason: 'The image is too uncertain to prefill safely.',
  });
});

test('rejects malformed model output instead of guessing defaults', () => {
  assert.throws(() => parseVisionResponse({ isStool: true, bristolType: 9, color: 'purple', confidence: 0.8 }), /invalid/i);
  assert.throws(() => parseVisionResponse('not json'), /invalid/i);
});

test('merges only visual fields and preserves user-reported context', () => {
  const form = { bristolType: 2, color: 'green', urgency: 3, discomfort: 2, note: 'user note', symptoms: { fever: true } };
  const merged = mergeVisualSuggestion(form, { status: 'suggestion', bristolType: 4, color: 'brown', confidence: 0.8 });
  assert.deepEqual(merged, { bristolType: 4, color: 'brown', urgency: 3, discomfort: 2, note: 'user note', symptoms: { fever: true } });
});

test('accepts bounded JPEG/PNG/WebP data URLs and rejects oversized or unsupported input', () => {
  const payload = validatePhotoPayload({ imageDataUrl: 'data:image/jpeg;base64,' + 'YQ==', consent: true });
  assert.equal(payload.mime, 'image/jpeg');
  assert.equal(payload.bytes, 1);
  assert.throws(() => validatePhotoPayload({ imageDataUrl: 'data:image/svg+xml;base64,PHN2Zz4=', consent: true }), /JPEG, PNG, or WebP/i);
  assert.throws(() => validatePhotoPayload({ imageDataUrl: 'data:image/jpeg;base64,YQ==', consent: false }), /consent/i);
  assert.throws(() => validatePhotoPayload({ imageDataUrl: 'data:image/jpeg;base64,' + 'A'.repeat(6_000_000), consent: true }), /too large/i);
});

test('builds a structured multimodal request that forbids diagnosis and nonvisual inference', () => {
  const body = buildVisionRequest({ imageDataUrl: 'data:image/jpeg;base64,YQ==', model: 'vision-model' });
  assert.equal(body.model, 'vision-model');
  assert.equal(body.response_format.type, 'json_schema');
  const prompt = body.messages[0].content.find(part => part.type === 'text').text;
  assert.match(prompt, /do not infer urgency/i);
  assert.match(prompt, /not a diagnosis/i);
  assert.equal(body.messages[0].content.find(part => part.type === 'image_url').image_url.url, 'data:image/jpeg;base64,YQ==');
});
