const { test } = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');

function loadVitals() {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'vitals.json'), 'utf8');
  return JSON.parse(raw);
}

test('vitals.json exposes all calculator age groups', () => {
  const data = loadVitals();
  assert.equal(data.version, '1.0');
  assert.deepEqual(Object.keys(data.ageGroups), [
    'premie',
    '0-3mo',
    '3-6mo',
    '6-12mo',
    '1-3yr',
    '3-6yr',
    '6-12yr',
    '>12yr'
  ]);
});

test('vitals.json preserves key Harriet Lane ranges', () => {
  const groups = loadVitals().ageGroups;
  assert.equal(groups.premie.hr.awake, '120-170');
  assert.equal(groups.premie.spo2, '88-95% (target)');
  assert.equal(groups['0-3mo'].sbp, '65-85');
  assert.equal(groups['3-6mo'].rr, '30-45');
  assert.equal(groups['1-3yr'].weight, '10-15 kg');
  assert.equal(groups['6-12yr'].hr.sleeping, '50-85');
  assert.equal(groups['>12yr'].dbp, '65-85');
});

test('vitals.json entries have complete renderable fields', () => {
  const groups = loadVitals().ageGroups;
  Object.entries(groups).forEach(([key, group]) => {
    assert.ok(group.label, `${key}.label`);
    assert.ok(group.hr && group.hr.awake && group.hr.sleeping, `${key}.hr`);
    ['rr', 'sbp', 'dbp', 'temp', 'weight', 'spo2'].forEach((field) => {
      assert.ok(group[field], `${key}.${field}`);
    });
    assert.ok(Array.isArray(group.notes) && group.notes.length > 0, `${key}.notes`);
  });
});

test('resus-meds.json exposes all PALS medication formulas', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'resus-meds.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  assert.deepEqual(data.medications.map((m) => m.formula), [
    'adenosine',
    'amiodarone',
    'atropine',
    'calciumChloride',
    'calciumGluconate',
    'dextrose',
    'epinephrine',
    'hydrocortisone',
    'insulin',
    'lidocaine',
    'magnesiumSulfate',
    'naloxone',
    'sodiumBicarbonate'
  ]);
});

test('resus dose formulas preserve representative outputs', async () => {
  const { calculateResusDose } = await import('../public/js/calculators/resus.js');
  assert.equal(calculateResusDose('adenosine', 20).dose, '2 mg (0.1 mg/kg)');
  assert.equal(calculateResusDose('epinephrine', 10).dose, '0.1 mg IV/IO (0.01 mg/kg of 0.1 mg/mL = 1 mL) q3-5 min');
  assert.equal(calculateResusDose('magnesiumSulfate', 50).dose, '2000 mg (50 mg/kg)');
  assert.equal(calculateResusDose('sodiumBicarbonate', 8).extra, 'Dilute to 0.5 mEq/mL (use 4.2% solution) for neonates/small infants.');
});

test('fenton-lms.json preserves key LMS anchors', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'fenton-lms.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  assert.deepEqual(data.weightLms.male['22'], { L: 0.21, M: 496, S: 0.17 });
  assert.deepEqual(data.weightLms.male['40'], { L: 0.01, M: 3530, S: 0.12 });
  assert.deepEqual(data.weightLms.female['22'], { L: 0.23, M: 474, S: 0.17 });
  assert.deepEqual(data.weightLms.female['40'], { L: 0.01, M: 3340, S: 0.12 });
});

test('neonatal Fenton helpers preserve interpolation and classification', async () => {
  const neonatal = await import('../public/js/calculators/neonatal.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'fenton-lms.json'), 'utf8');
  const data = JSON.parse(raw).weightLms;
  assert.deepEqual(neonatal.interpolateLMS(data.male, 30), { L: 0.18, M: 1430, S: 0.14 });
  assert.equal(Math.round(neonatal.interpolateLMS(data.female, 30.5).M), 1415);
  assert.equal(neonatal.classifyGA(36, 6).label, 'Late Preterm');
  assert.equal(neonatal.classifyBirthWeight(900).label, 'Extremely Low Birth Weight (ELBW)');
  assert.equal(neonatal.classifyWeight(50).label, 'AGA');
});

test('equipment.json preserves equipment age groups and anchors', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'equipment.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  assert.deepEqual(Object.keys(data.ageGroups), [
    'premie',
    'newborn',
    '6mo',
    '1yr',
    '2-3yr',
    '4-6yr',
    '7-10yr',
    '11-15yr',
    '16yr'
  ]);
  assert.equal(data.ageGroups.newborn.ett, '3.0-3.5');
  assert.equal(data.ageGroups['1yr'].lma, '2');
  assert.equal(data.ageGroups['4-6yr'].blade, 'Miller 2 / MAC 2');
  assert.equal(data.ageGroups['16yr'].foley, '12 Fr');
});

test('equipment.json entries have complete renderable fields', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'equipment.json'), 'utf8');
  const groups = JSON.parse(raw).ageGroups;
  Object.entries(groups).forEach(([key, group]) => {
    ['label', 'bvm', 'nasal', 'oral', 'blade', 'ett', 'lma', 'glidescope', 'iv', 'cvl', 'ngt', 'chest', 'foley'].forEach((field) => {
      assert.ok(group[field], `${key}.${field}`);
    });
  });
});

test('bhutani-zones.json preserves risk-zone curves', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bhutani-zones.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  assert.deepEqual(Object.keys(data.zones), ['p95', 'p75', 'p40']);
  assert.equal(data.zones.p95['24'], 9.6);
  assert.equal(data.zones.p75['48'], 12.6);
  assert.equal(data.zones.p40['120'], 13.2);
});

test('bilirubin helpers preserve Bhutani interpolation and classification', async () => {
  const bilirubin = await import('../public/js/calculators/bilirubin.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bhutani-zones.json'), 'utf8');
  const zones = JSON.parse(raw).zones;
  assert.equal(bilirubin.interpolateThreshold(zones.p95, 21), 9.05);
  assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 10).zone, 'High-Risk Zone');
  assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 8).zone, 'High-Intermediate Zone');
  assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 7).zone, 'Low-Intermediate Zone');
  assert.equal(bilirubin.classifyBhutaniRisk(zones, 24, 5).zone, 'Low-Risk Zone');
});

test('aap-bilirubin-thresholds.json preserves treatment threshold anchors', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  assert.deepEqual(Object.keys(data.phototherapy.noRisk), ['35', '36', '37', '38', '39', '40']);
  assert.deepEqual(Object.keys(data.phototherapy.risk), ['35', '36', '37', '38']);
  assert.deepEqual(Object.keys(data.exchange.noRisk), ['35', '36', '37', '38']);
  assert.deepEqual(Object.keys(data.exchange.risk), ['35', '36', '37', '38']);
  assert.equal(data.phototherapy.noRisk['35']['24'], 10.6);
  assert.equal(data.phototherapy.noRisk['40']['48'], 17.0);
  assert.equal(data.phototherapy.risk['38']['72'], 16.6);
  assert.equal(data.exchange.noRisk['38']['96'], 27.0);
  assert.equal(data.exchange.risk['35']['24'], 16.1);
});

test('bilirubin helpers preserve AAP 2022 GA plateau selection and interpolation', async () => {
  const bilirubin = await import('../public/js/calculators/bilirubin.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(bilirubin.interpolateThreshold(data.phototherapy.noRisk['35'], 12.5), 8.6);
  assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 41, 'none').phototherapy['48'], 17.0);
  assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 39, 'none').phototherapy['48'], 16.6);
  assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 41, 'medium').phototherapy['48'], 14.0);
  assert.equal(bilirubin.selectAapBilirubinThresholdTables(data, 40, 'none').exchange['48'], 24.0);
});

test('AAP bilirubin sample calculation below phototherapy threshold', async () => {
  const bilirubin = await import('../public/js/calculators/bilirubin.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
  const data = JSON.parse(raw);
  const assessment = bilirubin.calculateAapBilirubinAssessment(data, 40, 48, 16.0, 'none');
  assert.equal(assessment.phototherapyThreshold, 17.0);
  assert.equal(assessment.exchangeThreshold, 24.0);
  assert.equal(assessment.phototherapyMargin, -1.0);
  assert.equal(assessment.exchangeMargin, -8.0);
  assert.equal(assessment.abovePhototherapy, false);
  assert.equal(assessment.aboveExchange, false);
  assert.equal(assessment.status, 'below');
});

test('AAP bilirubin sample calculation above phototherapy threshold', async () => {
  const bilirubin = await import('../public/js/calculators/bilirubin.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
  const data = JSON.parse(raw);
  const assessment = bilirubin.calculateAapBilirubinAssessment(data, 39, 48, 17.0, 'none');
  assert.equal(assessment.phototherapyThreshold, 16.6);
  assert.equal(assessment.exchangeThreshold, 24.0);
  assert.equal(Math.round(assessment.phototherapyMargin * 10) / 10, 0.4);
  assert.equal(assessment.abovePhototherapy, true);
  assert.equal(assessment.aboveExchange, false);
  assert.equal(assessment.status, 'phototherapy');
});

test('AAP bilirubin sample calculation above exchange threshold', async () => {
  const bilirubin = await import('../public/js/calculators/bilirubin.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
  const data = JSON.parse(raw);
  const assessment = bilirubin.calculateAapBilirubinAssessment(data, 35, 24, 16.2, 'medium');
  assert.equal(assessment.phototherapyThreshold, 8.9);
  assert.equal(assessment.exchangeThreshold, 16.1);
  assert.equal(Math.round(assessment.phototherapyMargin * 10) / 10, 7.3);
  assert.equal(Math.round(assessment.exchangeMargin * 10) / 10, 0.1);
  assert.equal(assessment.abovePhototherapy, true);
  assert.equal(assessment.aboveExchange, true);
  assert.equal(assessment.status, 'exchange');
});

test('AAP bilirubin sample calculation interpolates fractional age', async () => {
  const bilirubin = await import('../public/js/calculators/bilirubin.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'aap-bilirubin-thresholds.json'), 'utf8');
  const data = JSON.parse(raw);
  const assessment = bilirubin.calculateAapBilirubinAssessment(data, 35, 12.5, 8.55, 'none');
  assert.equal(assessment.phototherapyThreshold, 8.6);
  assert.equal(assessment.abovePhototherapy, false);
  assert.equal(assessment.status, 'below');
});

test('bmi-lms.json exposes complete CDC BMI-for-age tables', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  ['male', 'female'].forEach((sex) => {
    const ages = Object.keys(data.lms[sex]).map(Number).sort((a, b) => a - b);
    assert.equal(ages[0], 24);
    assert.equal(ages[ages.length - 1], 240);
    assert.equal(ages.length, 37);
    ages.forEach((age) => {
      const row = data.lms[sex][age];
      assert.equal(typeof row.L, 'number', `${sex}.${age}.L`);
      assert.equal(typeof row.M, 'number', `${sex}.${age}.M`);
      assert.equal(typeof row.S, 'number', `${sex}.${age}.S`);
    });
  });
});

test('BMI sample calculation preserves healthy-weight result', async () => {
  const bmi = await import('../public/js/calculators/bmi.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
  const data = JSON.parse(raw).lms;
  const assessment = bmi.calculateBMIAssessment(data, 'male', 10, 32, 140);
  assert.equal(assessment.ageMonths, 120);
  assert.equal(Number(assessment.bmi.toFixed(1)), 16.3);
  assert.equal(Number(assessment.z.toFixed(2)), -0.17);
  assert.equal(assessment.percentile, 43.42);
  assert.equal(assessment.classification.text, 'Healthy Weight');
});

test('BMI sample calculation preserves obesity classification', async () => {
  const bmi = await import('../public/js/calculators/bmi.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
  const data = JSON.parse(raw).lms;
  const assessment = bmi.calculateBMIAssessment(data, 'male', 12, 70, 150);
  assert.equal(Number(assessment.bmi.toFixed(1)), 31.1);
  assert.equal(assessment.percentile, 98.97);
  assert.equal(assessment.classification.text, 'Class 2 Severe Obesity');
  assert.equal(Number(assessment.classification.pctOf95.toFixed(0)), 128);
  assert.equal(Number(assessment.classification.bmi95.toFixed(1)), 24.2);
});

test('BMI sample calculation preserves fractional-age interpolation', async () => {
  const bmi = await import('../public/js/calculators/bmi.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
  const data = JSON.parse(raw).lms;
  const lms = bmi.interpolateLMS(data.male, 123);
  assert.equal(Number(lms.M.toFixed(4)), 16.7806);
  const assessment = bmi.calculateBMIAssessment(data, 'male', 10.5, 40, 145);
  assert.equal(assessment.ageMonths, 126);
  assert.equal(assessment.percentile, 79.26);
  assert.equal(assessment.classification.text, 'Healthy Weight');
});

test('BMI calculator preserves legacy outputs across sex/age/body-size combinations', async () => {
  const bmi = await import('../public/js/calculators/bmi.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bmi-lms.json'), 'utf8');
  const data = JSON.parse(raw).lms;
  const cases = [
    { name: 'male healthy 10y', sex: 'male', ageYears: 10, weightKg: 32, heightCm: 140, ageMonths: 120, bmi: 16.3, z: -0.17, percentile: 43.42, classification: 'Healthy Weight', pctOf95: 74, bmi95: 22.2 },
    { name: 'female obese 8y', sex: 'female', ageYears: 8, weightKg: 35, heightCm: 130, ageMonths: 96, bmi: 20.7, z: 1.65, percentile: 95.03, classification: 'Obese (Class 1)', pctOf95: 100, bmi95: 20.7 },
    { name: 'male class 2 severe 12y', sex: 'male', ageYears: 12, weightKg: 70, heightCm: 150, ageMonths: 144, bmi: 31.1, z: 2.32, percentile: 98.97, classification: 'Class 2 Severe Obesity', pctOf95: 128, bmi95: 24.2 },
    { name: 'female underweight 5y', sex: 'female', ageYears: 5, weightKg: 13, heightCm: 110, ageMonths: 60, bmi: 10.7, z: -7.66, percentile: 0, classification: 'Underweight', pctOf95: 59, bmi95: 18.3 },
    { name: 'male fractional age 10.5y', sex: 'male', ageYears: 10.5, weightKg: 40, heightCm: 145, ageMonths: 126, bmi: 19.0, z: 0.82, percentile: 79.26, classification: 'Healthy Weight', pctOf95: 84, bmi95: 22.7 },
    { name: 'female healthy 15y', sex: 'female', ageYears: 15, weightKg: 52, heightCm: 162, ageMonths: 180, bmi: 19.8, z: -0.04, percentile: 48.44, classification: 'Healthy Weight', pctOf95: 70, bmi95: 28.1 },
    { name: 'male overweight 6y', sex: 'male', ageYears: 6, weightKg: 26, heightCm: 120, ageMonths: 72, bmi: 18.1, z: 1.51, percentile: 93.42, classification: 'Overweight', pctOf95: 98, bmi95: 18.4 },
    { name: 'female class 3 severe 14y', sex: 'female', ageYears: 14, weightKg: 100, heightCm: 155, ageMonths: 168, bmi: 41.6, z: 2.61, percentile: 99.55, classification: 'Class 3 Severe Obesity', pctOf95: 153, bmi95: 27.3 }
  ];

  cases.forEach((expected) => {
    const actual = bmi.calculateBMIAssessment(data, expected.sex, expected.ageYears, expected.weightKg, expected.heightCm);
    assert.equal(actual.ageMonths, expected.ageMonths, expected.name + ' ageMonths');
    assert.equal(Number(actual.bmi.toFixed(1)), expected.bmi, expected.name + ' bmi');
    assert.equal(Number(actual.z.toFixed(2)), expected.z, expected.name + ' z');
    assert.equal(actual.percentile, expected.percentile, expected.name + ' percentile');
    assert.equal(actual.classification.text, expected.classification, expected.name + ' classification');
    assert.equal(Number(actual.classification.pctOf95.toFixed(0)), expected.pctOf95, expected.name + ' pctOf95');
    assert.equal(Number(actual.classification.bmi95.toFixed(1)), expected.bmi95, expected.name + ' bmi95');
  });
});

test('growth-lms.json exposes complete WHO/CDC/Fenton datasets', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'growth-lms.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  assert.deepEqual(Object.keys(data.lms), ['wfaLMS', 'cdcWfaLMS', 'lfaLMS', 'cdcLfaLMS', 'hcfaLMS', 'wflLMS', 'fentonLMS']);
  Object.entries(data.lms).forEach(([tableName, bySex]) => {
    ['male', 'female'].forEach((sex) => {
      const rows = bySex[sex];
      assert.ok(rows, `${tableName}.${sex}`);
      const keys = Object.keys(rows).map(Number).sort((a, b) => a - b);
      assert.ok(keys.length > 0, `${tableName}.${sex}.rows`);
      keys.forEach((key) => {
        assert.equal(typeof rows[key].L, 'number', `${tableName}.${sex}.${key}.L`);
        assert.equal(typeof rows[key].M, 'number', `${tableName}.${sex}.${key}.M`);
        assert.equal(typeof rows[key].S, 'number', `${tableName}.${sex}.${key}.S`);
      });
    });
  });
});

test('growth calculator preserves legacy outputs across chart combinations', async () => {
  const growth = await import('../public/js/calculators/growth.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'growth-lms.json'), 'utf8');
  const data = JSON.parse(raw).lms;
  const cases = [
    { name: 'who wfa male 6mo 8kg', table: data.wfaLMS.male, x: 6, value: 8, z: 0.07, percentile: 52.79, lms: { L: 0.1256, M: 7.9389, S: 0.10957 } },
    { name: 'cdc wfa female 72mo 20kg', table: data.cdcWfaLMS.female, x: 72, value: 20, z: -0.11, percentile: 45.51, lms: { L: -1.326764, M: 20.3364, S: 0.14959 } },
    { name: 'who lfa male 12mo 75cm', table: data.lfaLMS.male, x: 12, value: 75, z: -0.31, percentile: 37.79, lms: { L: 1, M: 75.7391, S: 0.03137 } },
    { name: 'cdc lfa female 120mo 140cm', table: data.cdcLfaLMS.female, x: 120, value: 140, z: 0.26, percentile: 60.43, lms: { L: 0.284749, M: 138.2112, S: 0.048705 } },
    { name: 'who hcfa male 12mo 46cm', table: data.hcfaLMS.male, x: 12, value: 46, z: -0.05, percentile: 48.02, lms: { L: 1, M: 46.0637, S: 0.02789 } },
    { name: 'who wfl female 70cm 8kg', table: data.wflLMS.female, x: 70, value: 8, z: -0.22, percentile: 41.17, lms: { L: -0.3833, M: 8.163, S: 0.09068 } },
    { name: 'fenton male 30w 1.5kg', table: data.fentonLMS.male, x: 30, value: 1500, z: 0.34, percentile: 63.41, lms: { L: 0.18, M: 1430, S: 0.14 } }
  ];

  cases.forEach((expected) => {
    const actual = growth.calculateLmsPercentile(expected.table, expected.x, expected.value);
    assert.equal(Number(actual.z.toFixed(2)), expected.z, expected.name + ' z');
    assert.equal(Number(actual.percentile.toFixed(2)), expected.percentile, expected.name + ' percentile');
    assert.equal(Number(actual.lms.L.toFixed(6)), expected.lms.L, expected.name + ' L');
    assert.equal(Number(actual.lms.M.toFixed(4)), expected.lms.M, expected.name + ' M');
    assert.equal(Number(actual.lms.S.toFixed(6)), expected.lms.S, expected.name + ' S');
  });
});

test('growth helper exports preserve mid-parental height and result rendering', async () => {
  const growth = await import('../public/js/calculators/growth.js');
  assert.deepEqual(growth.calcMidParentalHeight(160, 180, 'male'), { target: 176.5, low: 168, high: 185 });
  assert.deepEqual(growth.calcMidParentalHeight(160, 180, 'female'), { target: 163.5, low: 155, high: 172 });
  assert.equal(Number(growth.valueFromLMS(1, 75, 0.03, 0).toFixed(2)), 75);
  const html = growth.renderGrowthResult('Weight-for-Age', 8, 'kg', 0.07, 52.79);
  assert.match(html, /52\.79<span style="font-size:12px;">th/);
  assert.match(html, /Weight-for-Age: 8 kg/);
});

test('bp-reference.json exposes complete height LMS and coefficient tables', () => {
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bp-reference.json'), 'utf8');
  const data = JSON.parse(raw);
  assert.equal(data.version, '1.0');
  ['female', 'male'].forEach((sex) => {
    ['L', 'M', 'S'].forEach((field) => {
      assert.equal(data.heightLms[sex][field].length, 218, `${sex}.${field}`);
      data.heightLms[sex][field].forEach((value, index) => {
        assert.equal(typeof value, 'number', `${sex}.${field}.${index}`);
      });
    });
    ['sys', 'dia'].forEach((type) => {
      assert.equal(data.coefficients[sex][type].length, 99, `${sex}.${type}`);
      data.coefficients[sex][type].forEach((row, index) => {
        assert.equal(row.length, 13, `${sex}.${type}.${index}`);
        row.forEach((value, col) => assert.equal(typeof value, 'number', `${sex}.${type}.${index}.${col}`));
      });
    });
  });
});

test('BP calculator preserves legacy outputs across sex/age/height/BP combinations', async () => {
  const bp = await import('../public/js/calculators/bp.js');
  const raw = fs.readFileSync(path.join(__dirname, '..', 'public', 'data', 'calculators', 'bp-reference.json'), 'utf8');
  const data = JSON.parse(raw);
  const cases = [
    { name: 'male child normal', age: 5, sex: 'male', heightCm: 110, sys: 95, dia: 55, heightPctile: 61.69, heightZ: 0.3, sysPctile: 56, diaPctile: 56, sys50: 93, sys90: 106, sys95: 109, dia50: 53, dia90: 65, dia95: 69, sysClass: 'normal', diaClass: 'normal', classification: 'normal' },
    { name: 'female child stage1 systolic', age: 8, sex: 'female', heightCm: 130, sys: 116, dia: 70, heightPctile: 67.28, heightZ: 0.45, sysPctile: 97, diaPctile: 86, sys50: 97, sys90: 110, sys95: 113, dia50: 59, dia90: 72, dia95: 75, sysClass: 'stage1', diaClass: 'normal', classification: 'stage1' },
    { name: 'male child stage1 both', age: 10, sex: 'male', heightCm: 140, sys: 120, dia: 80, heightPctile: 59.5, heightZ: 0.24, sysPctile: 98, diaPctile: 97, sys50: 100, sys90: 112, sys95: 116, dia50: 62, dia90: 75, dia95: 78, sysClass: 'stage1', diaClass: 'stage1', classification: 'stage1' },
    { name: 'female child stage2 absolute', age: 11, sex: 'female', heightCm: 145, sys: 142, dia: 92, heightPctile: 57.13, heightZ: 0.18, sysPctile: 99, diaPctile: 99, sys50: 102, sys90: 114, sys95: 118, dia50: 61, dia90: 74, dia95: 77, sysClass: 'stage2', diaClass: 'stage2', classification: 'stage2' },
    { name: 'male teen elevated', age: 14, sex: 'male', heightCm: 165, sys: 124, dia: 76, heightPctile: 57.25, heightZ: 0.18, sysPctile: 88, diaPctile: 89, sys50: 111, sys90: 125, sys95: 129, dia50: 63, dia90: 77, dia95: 80, sysClass: 'elevated', diaClass: 'normal', classification: 'elevated' },
    { name: 'female teen stage1 diastolic', age: 16, sex: 'female', heightCm: 162, sys: 118, dia: 82, heightPctile: 46.75, heightZ: -0.08, sysPctile: 79, diaPctile: 95, sys50: 109, sys90: 123, sys95: 127, dia50: 66, dia90: 78, dia95: 82, sysClass: 'normal', diaClass: 'stage1', classification: 'stage1' },
    { name: 'male teen stage2 systolic', age: 17, sex: 'male', heightCm: 175, sys: 142, dia: 78, heightPctile: 48.69, heightZ: -0.03, sysPctile: 98, diaPctile: 83, sys50: 117, sys90: 131, sys95: 136, dia50: 68, dia90: 81, dia95: 85, sysClass: 'stage2', diaClass: 'normal', classification: 'stage2' }
  ];

  cases.forEach((expected) => {
    const actual = bp.calculateBPAssessment(data, expected.age, expected.sex, expected.heightCm, expected.sys, expected.dia);
    assert.equal(Number(actual.height.percentile.toFixed(2)), expected.heightPctile, expected.name + ' height percentile');
    assert.equal(Number(actual.height.z.toFixed(2)), expected.heightZ, expected.name + ' height z');
    assert.equal(actual.systolic.percentile, expected.sysPctile, expected.name + ' systolic percentile');
    assert.equal(actual.diastolic.percentile, expected.diaPctile, expected.name + ' diastolic percentile');
    assert.equal(Number(actual.systolic.predicted[49].toFixed(0)), expected.sys50, expected.name + ' sys50');
    assert.equal(Number(actual.systolic.predicted[89].toFixed(0)), expected.sys90, expected.name + ' sys90');
    assert.equal(Number(actual.systolic.predicted[94].toFixed(0)), expected.sys95, expected.name + ' sys95');
    assert.equal(Number(actual.diastolic.predicted[49].toFixed(0)), expected.dia50, expected.name + ' dia50');
    assert.equal(Number(actual.diastolic.predicted[89].toFixed(0)), expected.dia90, expected.name + ' dia90');
    assert.equal(Number(actual.diastolic.predicted[94].toFixed(0)), expected.dia95, expected.name + ' dia95');
    assert.equal(actual.classification.sysClass, expected.sysClass, expected.name + ' sys class');
    assert.equal(actual.classification.diaClass, expected.diaClass, expected.name + ' dia class');
    assert.equal(actual.classification.classification, expected.classification, expected.name + ' classification');
  });
});

test('dosing helpers preserve BSA and weight-based dose outputs', async () => {
  const dosing = await import('../public/js/calculators/dosing.js');
  assert.equal(Number(dosing.calculateBsa(20, 110).toFixed(3)), 0.782);

  const uncapped = dosing.calculateWeightBasedDose(12, 15, 3, 0, 40);
  assert.equal(uncapped.singleDose, 180);
  assert.equal(uncapped.dailyDose, 540);
  assert.equal(uncapped.volumeMl, 4.5);
  assert.equal(uncapped.capped, false);

  const capped = dosing.calculateWeightBasedDose(40, 15, 4, 500, 50);
  assert.equal(capped.singleDose, 500);
  assert.equal(capped.dailyDose, 2000);
  assert.equal(capped.volumeMl, 10);
  assert.equal(capped.capped, true);
});

test('GCS helper preserves severity thresholds', async () => {
  const gcs = await import('../public/js/calculators/gcs.js');
  assert.deepEqual(gcs.calculateGcs(1, 1, 1), { total: 3, severity: 'Severe (Coma)', color: '#ef4444', bg: '#fee2e2' });
  assert.equal(gcs.calculateGcs(2, 3, 4).severity, 'Moderate');
  assert.equal(gcs.calculateGcs(4, 5, 6).severity, 'Mild');
});
