// ============================================================
// BEDSIDE PANELS (second batch) — neonatal, respiratory,
// ventilation, sepsis, burns. Completes parity with the 15
// vanilla Bedside sub-modules. Drug per-kg + max values ported
// byte-for-byte from public/js/bedside/<module>.js.
// ============================================================

import { useState } from 'react';
import { formatDose } from '@shared/clinical/calculators';
import { neonatalAssess, type Sex } from '@shared/clinical/fenton';

const card = 'rounded-lg border border-border bg-card p-5 space-y-3';
const input = 'w-full rounded-md border border-input bg-background px-3 py-2 text-sm outline-none focus:ring-2 focus:ring-ring';
const label = 'block text-xs font-medium text-muted-foreground';
const th = 'text-left px-2 py-1.5 border-b border-border font-semibold uppercase tracking-wide text-[10px] text-muted-foreground';
const td = 'px-2 py-1.5 border-b border-border align-top text-sm';

function Dose({ label: l }: { label: string }) {
  const i = l.indexOf('(');
  if (i < 0) return <span className="font-semibold">{l}</span>;
  return <span><span className="font-semibold">{l.slice(0, i).trim()}</span>{' '}<span className="text-xs text-muted-foreground">{l.slice(i)}</span></span>;
}

function DrugTable({ children, notes = true }: { children: React.ReactNode; notes?: boolean }) {
  return (
    <div className="overflow-x-auto">
      <table className="w-full text-sm">
        <thead><tr><th className={th}>Drug</th><th className={th}>Dose</th><th className={th}>Route</th>{notes && <th className={th}>Notes</th>}</tr></thead>
        <tbody>{children}</tbody>
      </table>
    </div>
  );
}

function Row({ name, dose, route, notes }: { name: string; dose: React.ReactNode; route: string; notes?: string }) {
  return (
    <tr>
      <td className={td + ' font-semibold'} dangerouslySetInnerHTML={{ __html: name }} />
      <td className={td}>{dose}</td>
      <td className={td + ' text-xs'}>{route}</td>
      {notes !== undefined && <td className={td + ' text-xs text-muted-foreground'} dangerouslySetInnerHTML={{ __html: notes }} />}
    </tr>
  );
}

// ── Neonatal ────────────────────────────────────────────────
export function NeonatalPanel() {
  const [weeks, setWeeks] = useState('');
  const [days, setDays] = useState('0');
  const [wtG, setWtG] = useState('');
  const [sex, setSex] = useState<Sex>('male');
  const [kgForNrp, setKgForNrp] = useState('');
  const [apgarScores, setApgarScores] = useState<Record<string, number>>({ appearance: 2, pulse: 2, grimace: 2, activity: 2, respiration: 2 });

  const weeksNum = Number.parseInt(weeks, 10);
  const daysNum = Number.parseInt(days, 10) || 0;
  const wtNum = Number.parseFloat(wtG);
  const validAssess = Number.isFinite(weeksNum) && weeksNum >= 22 && weeksNum <= 44 && Number.isFinite(wtNum) && wtNum > 0;
  const assess = validAssess ? neonatalAssess(weeksNum, daysNum, wtNum, sex) : null;

  const kg = Number.parseFloat(kgForNrp);
  const validKg = Number.isFinite(kg) && kg > 0;
  const epiIvLow  = validKg ? Math.round(kg * 0.01 * 100) / 100 : 0;
  const epiIvHigh = validKg ? Math.round(kg * 0.03 * 100) / 100 : 0;
  const epiEtLow  = validKg ? Math.round(kg * 0.05 * 100) / 100 : 0;
  const epiEtHigh = validKg ? Math.round(kg * 0.1 * 100) / 100 : 0;
  const ns = validKg ? Math.round(kg * 10) : 0;
  const d10 = validKg ? Math.round(kg * 2 * 10) / 10 : 0;

  const apgarTotal = Object.values(apgarScores).reduce((s, v) => s + v, 0);
  const apgarSeverity = apgarTotal >= 7 ? 'Reassuring' : apgarTotal >= 4 ? 'Moderately depressed' : 'Severely depressed';
  const apgarColor = apgarTotal >= 7 ? 'text-green-600 bg-green-50' : apgarTotal >= 4 ? 'text-amber-600 bg-amber-50' : 'text-destructive bg-red-50';
  const apgarGuidance = apgarTotal >= 7
    ? 'Routine newborn care. Continue reassessment. Repeat at 5 min.'
    : apgarTotal >= 4
    ? 'Stimulate, clear airway, warm. Give O₂ if cyanotic. Ventilate with PPV if HR <100 or apneic/gasping. Reassess q30 sec.'
    : 'Full NRP pathway — PPV immediately. Intubate if PPV ineffective. Chest compressions if HR <60. Epinephrine and volume per NRP.';

  return (
    <section className={card} data-testid="bedside-panel-neonatal">
      <h2 className="text-lg font-semibold">Neonatal Assessment + NRP + Apgar</h2>

      {/* Assessment */}
      <h3 className="text-sm font-semibold">Gestational age + size assessment (Fenton 2013)</h3>
      <div className="grid gap-2 grid-cols-2 sm:grid-cols-4 max-w-xl">
        <div><label className={label}>GA weeks</label><input type="number" min="22" max="44" className={input} value={weeks} onChange={(e) => setWeeks(e.target.value)} data-testid="neo-weeks" /></div>
        <div><label className={label}>GA days (0-6)</label><input type="number" min="0" max="6" className={input} value={days} onChange={(e) => setDays(e.target.value)} data-testid="neo-days" /></div>
        <div><label className={label}>Birth wt (g)</label><input type="number" min="200" max="7000" className={input} value={wtG} onChange={(e) => setWtG(e.target.value)} data-testid="neo-weight" /></div>
        <div><label className={label}>Sex</label><select className={input} value={sex} onChange={(e) => setSex(e.target.value as Sex)} data-testid="neo-sex"><option value="male">Male</option><option value="female">Female</option></select></div>
      </div>
      {assess && (
        <div className="grid gap-3 sm:grid-cols-2" data-testid="neo-result">
          <div className="rounded-md border p-3" style={{ borderColor: assess.gaClass.color + '55', background: assess.gaClass.color + '10' }}>
            <div className="text-xs text-muted-foreground">Gestational Age</div>
            <div className="text-base font-bold" style={{ color: assess.gaClass.color }}>{assess.gaClass.label}</div>
            <div className="text-xs text-muted-foreground">{weeksNum} wk {daysNum} d ({assess.gaDecimal.toFixed(1)} wk)</div>
          </div>
          <div className="rounded-md border p-3" style={{ borderColor: assess.weightClass.color + '55', background: assess.weightClass.color + '10' }}>
            <div className="text-xs text-muted-foreground">Weight for Gestational Age</div>
            <div className="text-base font-bold" style={{ color: assess.weightClass.color }}>{assess.weightClass.label}</div>
            <div className="text-xs text-muted-foreground">{assess.percentile.toFixed(1)}th percentile · {assess.weightClass.detail}</div>
          </div>
          <div className="rounded-md border p-3" style={{ borderColor: assess.bwClass.color + '55', background: assess.bwClass.color + '10' }}>
            <div className="text-xs text-muted-foreground">Birth Weight Category</div>
            <div className="text-base font-bold" style={{ color: assess.bwClass.color }}>{assess.bwClass.label}</div>
            <div className="text-xs text-muted-foreground">{wtNum} g ({(wtNum / 1000).toFixed(2)} kg)</div>
          </div>
          <div className="rounded-md border border-border bg-muted/40 p-3 text-xs">
            <div className="text-xs text-muted-foreground uppercase tracking-wide">Fenton ({sex})</div>
            <div className="space-y-0.5 mt-1">
              <div><strong>Expected weight (M):</strong> {assess.expectedWeight} g</div>
              <div><strong>Z-score:</strong> {assess.z.toFixed(2)}</div>
              <div><strong>Percentile:</strong> {assess.percentile.toFixed(1)}%</div>
            </div>
          </div>
        </div>
      )}

      {/* NRP pathway */}
      <h3 className="text-sm font-semibold mt-3">NRP pathway (AHA/AAP 8th ed 2020)</h3>
      <div className="space-y-2 text-sm">
        <div className="rounded-md border-l-4 border-blue-500 bg-blue-50 dark:bg-blue-950/30 p-3"><div className="font-semibold">BIRTH — ASSESS (first 30 sec)</div><div className="text-xs text-muted-foreground">Term? Tone? Breathing/crying? All yes → routine care. Any no → warm, dry, stimulate, clear airway PRN, evaluate HR + resp.</div></div>
        <div className="rounded-md border-l-4 border-purple-500 bg-purple-50 dark:bg-purple-950/30 p-3"><div className="font-semibold">HR &lt;100 OR apneic/gasping (60 s)</div><div className="text-xs text-muted-foreground"><strong>Start PPV</strong> 40-60 breaths/min, room air for term / 21-30% for preterm. Attach SpO₂ (right hand) ± ECG. MR SOPA if ineffective.</div></div>
        <div className="rounded-md border-l-4 border-amber-500 bg-amber-50 dark:bg-amber-950/30 p-3"><div className="font-semibold">HR &lt;100 after 30 s effective PPV</div><div className="text-xs text-muted-foreground">Reassess ventilation — ensure chest rise. Consider increasing FiO₂, intubation, or LMA. Continue PPV.</div></div>
        <div className="rounded-md border-l-4 border-red-500 bg-red-50 dark:bg-red-950/30 p-3"><div className="font-semibold">HR &lt;60 after 30 s effective PPV</div><div className="text-xs text-muted-foreground"><strong>Intubate + chest compressions</strong> — 3:1 ratio (90 compressions + 30 breaths/min), FiO₂ 100%, lower 1/3 sternum, depth 1/3 AP chest.</div></div>
        <div className="rounded-md border-l-4 border-destructive bg-red-100 dark:bg-red-950/40 p-3"><div className="font-semibold">HR &lt;60 despite compressions + PPV × 60 s</div><div className="text-xs text-muted-foreground"><strong>Epinephrine 1:10,000 (0.1 mg/mL):</strong> IV/IO 0.01-0.03 mg/kg (0.1-0.3 mL/kg) — preferred. ETT 0.05-0.1 mg/kg. Repeat q3-5 min. Hypovolemia: <strong>NS 10 mL/kg IV/IO over 5-10 min</strong>.</div></div>
      </div>
      <div className="grid grid-cols-1 sm:grid-cols-3 gap-2 text-xs">
        <div className="rounded-md bg-muted/40 p-2"><strong>Target SpO₂ (preductal):</strong><br />1 min 60-65% · 2 min 65-70% · 3 min 70-75% · 4 min 75-80% · 5 min 80-85% · 10 min 85-95%</div>
        <div className="rounded-md bg-muted/40 p-2"><strong>Initial ETT size:</strong><br />&lt;1 kg / &lt;28 wk: 2.5 · 1-2 kg / 28-34 wk: 3.0 · 2-3 kg / 34-38 wk: 3.5 · &gt;3 kg / &gt;38 wk: 3.5-4.0</div>
        <div className="rounded-md bg-muted/40 p-2"><strong>ETT depth (lip):</strong> ~6 + weight(kg) cm</div>
      </div>

      <h3 className="text-sm font-semibold mt-3">NRP drug doses</h3>
      <div className="max-w-xs"><label className={label}>Weight (kg)</label><input type="number" min="0.3" step="0.1" className={input} value={kgForNrp} onChange={(e) => setKgForNrp(e.target.value)} data-testid="nrp-weight" /></div>
      {validKg ? (
        <DrugTable>
          <Row name="Epinephrine 1:10,000" dose={<Dose label={`${epiIvLow}-${epiIvHigh} mg, ${Math.round(epiIvLow * 10) / 10}-${Math.round(epiIvHigh * 10) / 10} mL (0.01-0.03 mg/kg = 0.1-0.3 mL/kg)`} />} route="IV / IO" notes="Preferred route. Repeat q3-5 min." />
          <Row name="Epinephrine 1:10,000" dose={<Dose label={`${epiEtLow}-${epiEtHigh} mg, ${Math.round(epiEtLow * 10) / 10}-${Math.round(epiEtHigh * 10) / 10} mL (0.05-0.1 mg/kg = 0.5-1 mL/kg)`} />} route="ETT" notes="While IV being placed." />
          <Row name="Normal saline" dose={<Dose label={`${ns} mL (10 mL/kg)`} />} route="IV / IO" notes="Over 5-10 min for volume. Repeat PRN." />
          <Row name="Dextrose 10%" dose={<Dose label={`${d10} mL (2 mL/kg = 0.2 g/kg)`} />} route="IV slow push" notes="For documented hypoglycemia. Then D10 infusion 4-6 mg/kg/min." />
        </DrugTable>
      ) : <p className="text-xs text-destructive">Enter weight (kg) to see NRP doses.</p>}
      <div className="rounded-md bg-amber-50 dark:bg-amber-950/30 p-3 text-xs text-amber-900 dark:text-amber-100">
        <strong>Concentration note:</strong> NRP uses epinephrine <strong>1:10,000</strong> (0.1 mg/mL). NOT 1:1000 (1 mg/mL) — that is IM for anaphylaxis / older patients.
      </div>

      {/* Apgar */}
      <h3 className="text-sm font-semibold mt-3">Apgar score</h3>
      <div className="grid gap-2 grid-cols-1 sm:grid-cols-5 text-xs">
        {[
          ['appearance', 'Appearance', ['Blue/pale', 'Body pink, extremities blue', 'All pink']],
          ['pulse',       'Pulse',       ['Absent',     '<100 bpm',                '≥100 bpm']],
          ['grimace',     'Grimace',     ['No response', 'Grimace',                  'Cough/sneeze']],
          ['activity',    'Activity',    ['Limp',       'Some flexion',             'Active motion']],
          ['respiration', 'Respiration', ['Absent',     'Slow/irregular',           'Good/crying']],
        ].map(([key, lab, options]) => (
          <div key={key as string}>
            <label className={label}>{lab}</label>
            <select className={input} value={apgarScores[key as string]} onChange={(e) => setApgarScores({ ...apgarScores, [key as string]: Number(e.target.value) })} data-testid={'apgar-' + key}>
              {(options as string[]).map((o, i) => <option key={i} value={i}>{i} — {o}</option>)}
            </select>
          </div>
        ))}
      </div>
      <div className={'rounded-md p-3 ' + apgarColor} data-testid="apgar-result">
        <div className="text-base font-bold">Apgar: {apgarTotal}/10 — {apgarSeverity}</div>
        <div className="text-xs text-muted-foreground mt-1">{apgarGuidance}</div>
      </div>
      <div className="text-xs text-muted-foreground italic">
        Fenton TR, Kim JH. BMC Pediatr 2013;13:59 · NRP 8th ed (AHA/AAP 2020) · Apgar is a description of status — <strong>never</strong> delay resuscitation while scoring.
      </div>
    </section>
  );
}

// ── Respiratory ─────────────────────────────────────────────
export function RespiratoryPanel() {
  const [mode, setMode] = useState<'asthma' | 'pram' | 'croup' | 'bronch'>('asthma');
  const [weight, setWeight] = useState('');
  const wt = Number.parseFloat(weight);
  const valid = Number.isFinite(wt) && wt > 0;
  const f = (perKg: number, max: number | null, unit = 'mg') => (valid ? formatDose(wt, perKg, max, unit) : null);
  const [asthmaSev, setAsthmaSev] = useState<'mild' | 'moderate' | 'severe' | null>(null);

  // PRAM inputs (0-12 total)
  const [pram, setPram] = useState({ spo2: 0, retractions: 0, scalene: 0, air: 0, wheeze: 0 });
  const pramTotal = Object.values(pram).reduce((s, v) => s + v, 0);
  const pramSev = pramTotal <= 3 ? 'Mild' : pramTotal <= 7 ? 'Moderate' : 'Severe';
  const pramColor = pramTotal <= 3 ? 'text-green-600 bg-green-50' : pramTotal <= 7 ? 'text-amber-600 bg-amber-50' : 'text-destructive bg-red-50';

  // Croup / Westley (0-17)
  const [croup, setCroup] = useState({ conscious: 0, cyanosis: 0, stridor: 0, air: 0, retractions: 0 });
  const croupTotal = Object.values(croup).reduce((s, v) => s + v, 0);
  const croupSev = croupTotal <= 2 ? 'Mild' : croupTotal <= 5 ? 'Moderate' : croupTotal <= 11 ? 'Severe' : 'Impending Respiratory Failure';
  const croupColor = croupTotal <= 2 ? 'text-green-600 bg-green-50' : croupTotal <= 5 ? 'text-amber-600 bg-amber-50' : croupTotal <= 11 ? 'text-destructive bg-red-50' : 'text-red-900 bg-red-100';

  // Bronchiolitis inputs
  const [bronch, setBronch] = useState({ age: 'gte12w', spo2: 'ok', hydration: 'ok', distress: 'mild' });
  const bronchAdmit = bronch.distress === 'severe' || bronch.spo2 === 'low' || bronch.hydration === 'poor' || bronch.age === 'lt12w';

  return (
    <section className={card} data-testid="bedside-panel-respiratory">
      <h2 className="text-lg font-semibold">Respiratory</h2>
      <div className="flex gap-2 flex-wrap">
        {(['asthma', 'pram', 'croup', 'bronch'] as const).map((m) => (
          <button key={m} type="button" onClick={() => setMode(m)} className={'px-3 py-1 rounded-full text-xs font-medium border ' + (mode === m ? 'bg-primary text-primary-foreground border-primary' : 'bg-muted border-border')} data-testid={'resp-mode-' + m}>
            {m === 'asthma' ? 'Asthma' : m === 'pram' ? 'PRAM' : m === 'croup' ? 'Croup (Westley)' : 'Bronchiolitis'}
          </button>
        ))}
      </div>

      {mode !== 'pram' && mode !== 'bronch' && (
        <div className="max-w-xs"><label className={label}>Weight (kg)</label><input type="number" min="0.3" step="0.1" className={input} value={weight} onChange={(e) => setWeight(e.target.value)} data-testid="resp-weight" /></div>
      )}

      {/* ASTHMA */}
      {mode === 'asthma' && (
        <>
          <div className="flex gap-2">
            {(['mild', 'moderate', 'severe'] as const).map((s) => (
              <button key={s} type="button" onClick={() => setAsthmaSev(s)} className={'px-3 py-1 rounded text-xs font-medium border ' + (asthmaSev === s ? (s === 'mild' ? 'bg-green-600 text-white' : s === 'moderate' ? 'bg-amber-500 text-white' : 'bg-destructive text-white') : 'bg-muted')}>
                {s.charAt(0).toUpperCase() + s.slice(1)}
              </button>
            ))}
          </div>
          {asthmaSev && !valid && <p className="text-xs text-destructive">Enter weight (kg) to see doses.</p>}
          {asthmaSev === 'mild' && valid && (
            <>
              <p className="text-xs text-muted-foreground">Speaks in sentences, no accessory muscle use, SpO₂ ≥94%</p>
              <DrugTable>
                <Row name="Albuterol (MDI)"     dose="4-8 puffs via spacer" route="Inhaled"    notes="q20min × 3 doses, then q1-4h" />
                <Row name="Albuterol (neb)"     dose={<Dose label={`${f(0.15, 5, 'mg')!.label} (min 2.5 mg)`} />} route="Nebulized" notes="q20min × 3 doses" />
                <Row name="Dexamethasone"       dose={<Dose label={f(0.6, 16)!.label} />}  route="PO/IV" notes="Single dose, or 2 days" />
                <Row name="Prednisolone"        dose={<Dose label={`${f(1, 60)!.label}/day`} />} route="PO"    notes="Alternative: 3-5 day course" />
              </DrugTable>
            </>
          )}
          {asthmaSev === 'moderate' && valid && (
            <>
              <p className="text-xs text-muted-foreground">Speaks in phrases, some accessory muscle use, SpO₂ 90-93%</p>
              <DrugTable>
                <Row name="Albuterol (neb)"     dose={<Dose label={`${f(0.15, 5, 'mg')!.label} (min 2.5 mg)`} />} route="Nebulized" notes="q20min × 3 doses, then continuous if needed" />
                <Row name="Ipratropium"         dose={wt < 20 ? '250 mcg' : '500 mcg'}     route="Nebulized" notes="q20min × 3 doses with albuterol" />
                <Row name="Dexamethasone"       dose={<Dose label={f(0.6, 16)!.label} />} route="PO/IV/IM"  notes="Single dose" />
                <Row name="O₂ supplemental"     dose="Target SpO₂ ≥94%"                    route="NC/mask"   notes="Titrate to effect" />
              </DrugTable>
            </>
          )}
          {asthmaSev === 'severe' && valid && (
            <>
              <p className="text-xs text-muted-foreground">Speaks in words only, significant accessory muscle use, SpO₂ &lt;90%. Consider ICU.</p>
              <DrugTable>
                <Row name="Albuterol continuous" dose={<Dose label={`${f(0.5, 20, 'mg')!.label}/hr`} />} route="Continuous neb" notes="Or 0.15-0.3 mg/kg q20min" />
                <Row name="Ipratropium"          dose={wt < 20 ? '250 mcg' : '500 mcg'}                 route="Nebulized"      notes="q20min × 3 doses with albuterol" />
                <Row name="Dexamethasone"        dose={<Dose label={f(0.6, 16)!.label} />}              route="IV"             notes="Or methylprednisolone 2 mg/kg IV (max 60 mg)" />
                <Row name="Magnesium sulfate"   dose={<Dose label={`${f(50, 2000)!.label} IV over 20 min`} />} route="IV" notes="Single dose, monitor BP" />
                <Row name="Epinephrine (IM)"     dose={<Dose label={`${f(0.01, 0.5)!.label} (1:1000)`} />}  route="IM"  notes="If impending arrest / no IV access" />
                <Row name="Terbutaline"         dose={<Dose label={`${f(0.01, 0.4)!.label} SC/IV`} />}  route="SC/IV"          notes="Then 0.1-10 mcg/kg/min infusion" />
                <Row name="O₂ supplemental"     dose="Target SpO₂ ≥94%"                                 route="High flow / NIPPV" notes="Consider BiPAP/CPAP" />
              </DrugTable>
              <div className="rounded-md bg-red-50 dark:bg-red-950/30 p-3 text-xs text-red-900 dark:text-red-100">
                <strong>Continuous monitoring.</strong> Consider ICU admission. If no response to magnesium → terbutaline infusion. If impending respiratory failure → intubation (ketamine preferred induction agent).
              </div>
            </>
          )}
          <div className="text-xs text-muted-foreground italic">NAEPP/GINA guidelines. Always use clinical judgment.</div>
        </>
      )}

      {/* PRAM */}
      {mode === 'pram' && (
        <>
          <p className="text-sm text-muted-foreground">Pediatric Respiratory Assessment Measure (PRAM) — for asthma exacerbation severity (0-12).</p>
          <div className="grid gap-2 grid-cols-1 sm:grid-cols-2">
            {[
              ['spo2',        'SpO₂',               ['≥95% (0)', '92-94% (1)', '<92% (2)']],
              ['retractions', 'Suprasternal retractions', ['Absent (0)', 'Present (2)']],
              ['scalene',     'Scalene muscle use', ['Absent (0)', 'Present (2)']],
              ['air',         'Air entry',          ['Normal (0)', 'Mild ↓ at bases (1)', 'Widespread ↓ (2)', 'Absent/minimal (3)']],
              ['wheeze',      'Wheezing',           ['Absent (0)', 'Expiratory only (1)', 'Ins+exp (2)', 'Audible without stethoscope/silent chest (3)']],
            ].map(([key, lab, options]) => (
              <div key={key as string}>
                <label className={label}>{lab}</label>
                <select className={input} value={pram[key as keyof typeof pram]} onChange={(e) => setPram({ ...pram, [key as string]: Number(e.target.value) })} data-testid={'pram-' + key}>
                  {(options as string[]).map((o, i) => <option key={i} value={i}>{o}</option>)}
                </select>
              </div>
            ))}
          </div>
          <div className={'rounded-md p-3 ' + pramColor} data-testid="pram-result">
            <div className="text-base font-bold">PRAM Score: {pramTotal}/12 — {pramSev}</div>
            <div className="text-xs text-muted-foreground mt-1">Mild (0-3): outpatient management. Moderate (4-7): consider oral steroids + frequent bronchodilators. Severe (8-12): aggressive treatment, consider ICU.</div>
          </div>
        </>
      )}

      {/* CROUP */}
      {mode === 'croup' && (
        <>
          <p className="text-sm text-muted-foreground">Westley croup score (0-17).</p>
          <div className="grid gap-2 grid-cols-1 sm:grid-cols-2">
            {[
              ['conscious',   'Level of consciousness', ['Normal (0)', 'Disoriented (5)']],
              ['cyanosis',    'Cyanosis',               ['None (0)', 'With agitation (4)', 'At rest (5)']],
              ['stridor',     'Stridor',                ['None (0)', 'With agitation (1)', 'At rest (2)']],
              ['air',         'Air entry',              ['Normal (0)', 'Decreased (1)', 'Severely decreased (2)']],
              ['retractions', 'Retractions',            ['None (0)', 'Mild (1)', 'Moderate (2)', 'Severe (3)']],
            ].map(([key, lab, options]) => (
              <div key={key as string}>
                <label className={label}>{lab}</label>
                <select className={input} value={croup[key as keyof typeof croup]} onChange={(e) => setCroup({ ...croup, [key as string]: Number(e.target.value) })} data-testid={'croup-' + key}>
                  {(options as string[]).map((o, i) => <option key={i} value={i}>{o}</option>)}
                </select>
              </div>
            ))}
          </div>
          <div className={'rounded-md p-3 ' + croupColor} data-testid="croup-result">
            <div className="text-base font-bold">Westley: {croupTotal}/17 — {croupSev}</div>
            <div className="text-xs text-muted-foreground mt-1">Mild ≤2 · Moderate 3-5 · Severe 6-11 · Impending failure ≥12.</div>
          </div>
          {valid && (
            <DrugTable>
              <Row name="Dexamethasone" dose={<Dose label={f(0.6, 16)!.label} />} route={croupTotal <= 2 ? 'PO' : croupTotal <= 5 ? 'PO/IM' : 'IV/IM'} notes="Preferred corticosteroid; single dose" />
              {croupTotal > 2 && <Row name="Racemic epinephrine" dose="0.5 mL of 2.25% solution" route="Nebulized" notes="May repeat q15-20min, observe 2-4 h" />}
              {croupTotal > 2 && <Row name="Nebulized epinephrine" dose="0.5 mL/kg of 1:1000 (max 5 mL)" route="Nebulized" notes="Alternative to racemic" />}
              {croupTotal > 5 && <Row name="Heliox" dose="70:30 or 80:20" route="Face mask" notes="Consider if not responding" />}
            </DrugTable>
          )}
        </>
      )}

      {/* BRONCHIOLITIS */}
      {mode === 'bronch' && (
        <>
          <div className="grid gap-2 grid-cols-1 sm:grid-cols-2 max-w-xl">
            <div><label className={label}>Age</label><select className={input} value={bronch.age} onChange={(e) => setBronch({ ...bronch, age: e.target.value })}><option value="lt12w">&lt;12 weeks (high risk)</option><option value="gte12w">≥12 weeks</option></select></div>
            <div><label className={label}>SpO₂</label><select className={input} value={bronch.spo2} onChange={(e) => setBronch({ ...bronch, spo2: e.target.value })}><option value="ok">≥90%</option><option value="low">&lt;90%</option></select></div>
            <div><label className={label}>Hydration</label><select className={input} value={bronch.hydration} onChange={(e) => setBronch({ ...bronch, hydration: e.target.value })}><option value="ok">Adequate</option><option value="poor">Poor oral intake</option></select></div>
            <div><label className={label}>Distress</label><select className={input} value={bronch.distress} onChange={(e) => setBronch({ ...bronch, distress: e.target.value })}><option value="mild">Mild</option><option value="moderate">Moderate</option><option value="severe">Severe</option></select></div>
          </div>
          <div className={'rounded-md p-3 ' + (bronchAdmit ? 'text-destructive bg-red-50' : 'text-green-600 bg-green-50')} data-testid="bronch-result">
            <div className="text-base font-bold">{bronchAdmit ? 'Admit / Observe' : 'Likely Safe for Discharge'}</div>
            {bronch.age === 'lt12w' && <div className="text-xs text-destructive mt-1">⚠ Age &lt;12 weeks — high risk for apnea. Monitor closely.</div>}
          </div>
          <div className="rounded-md bg-amber-50 dark:bg-amber-950/30 p-3 text-xs text-amber-900 dark:text-amber-100">
            <strong>NOT recommended (AAP 2014/2023):</strong> Albuterol/salbutamol (no benefit), epinephrine (no evidence), systemic corticosteroids (no benefit), antibiotics (unless bacterial co-infection), chest physiotherapy.
          </div>
          <div className="text-xs text-muted-foreground italic">AAP Clinical Practice Guideline: Management of Bronchiolitis in Infants and Children (2014, reaffirmed 2023). RSV most common (50-80%).</div>
        </>
      )}
    </section>
  );
}

// ── Ventilation (O₂ escalation + vent settings reference) ───
export function VentilationPanel() {
  const [weight, setWeight] = useState('');
  const [age, setAge] = useState('');
  const wt = Number.parseFloat(weight);
  const ageY = Number.parseFloat(age);
  const validWt = Number.isFinite(wt) && wt > 0;
  const hfLow = validWt ? Math.round(wt * 1 * 10) / 10 : 0;
  const hfHigh = validWt ? Math.round(wt * 2 * 10) / 10 : 0;
  const tvLow = validWt ? Math.round(wt * 6 * 10) / 10 : 0;
  const tvHigh = validWt ? Math.round(wt * 8 * 10) / 10 : 0;
  const hasAge = Number.isFinite(ageY) && ageY >= 0;
  const rate = hasAge
    ? ageY < 0.1 ? '30-40'
    : ageY < 1 ? '25-35'
    : ageY < 5 ? '20-25'
    : ageY < 12 ? '16-20'
    : '12-16'
    : '';

  return (
    <section className={card} data-testid="bedside-panel-ventilation">
      <h2 className="text-lg font-semibold">O₂ &amp; Ventilation</h2>
      <div className="grid gap-2 grid-cols-2 max-w-md">
        <div><label className={label}>Weight (kg)</label><input type="number" min="0.3" step="0.1" className={input} value={weight} onChange={(e) => setWeight(e.target.value)} data-testid="vent-weight" /></div>
        <div><label className={label}>Age (years)</label><input type="number" min="0" step="0.5" className={input} value={age} onChange={(e) => setAge(e.target.value)} data-testid="vent-age" /></div>
      </div>

      <h3 className="text-sm font-semibold">Target SpO₂</h3>
      <DrugTable notes>
        <Row name="Most children"                 dose="94-98%" route="—" notes="Normal" />
        <Row name="Bronchiolitis (AAP 2014/2023)" dose="≥90%"   route="—" notes="Don't chase higher saturations" />
        <Row name="Chronic lung disease / CF"      dose="90-94%" route="—" notes="Avoid hyperoxia in CO₂ retainers" />
        <Row name="Preterm neonate"                dose="90-95%" route="—" notes="Minimize ROP risk" />
        <Row name="Term neonate (min of life)"    dose="Per NRP ladder" route="—" notes="1 min 60-65% · 10 min 85-95%" />
      </DrugTable>

      <h3 className="text-sm font-semibold mt-2">Escalation ladder</h3>
      <div className="space-y-2 text-sm">
        <div className="rounded-md border-l-4 border-green-500 bg-green-50 dark:bg-green-950/30 p-3"><div className="font-semibold">1. Nasal cannula (low-flow)</div><div className="text-xs text-muted-foreground"><strong>0.5-6 L/min</strong> · FiO₂ ~24-40% · comfortable, no humidification. Good for mild hypoxia.</div></div>
        <div className="rounded-md border-l-4 border-blue-500 bg-blue-50 dark:bg-blue-950/30 p-3"><div className="font-semibold">2. Simple face mask</div><div className="text-xs text-muted-foreground"><strong>6-10 L/min</strong> · FiO₂ 35-60%. Must keep flow &gt;6 L/min to flush CO₂.</div></div>
        <div className="rounded-md border-l-4 border-purple-500 bg-purple-50 dark:bg-purple-950/30 p-3"><div className="font-semibold">3. Non-rebreather mask</div><div className="text-xs text-muted-foreground"><strong>10-15 L/min</strong> · FiO₂ 60-90%. Reservoir bag must stay inflated.</div></div>
        <div className="rounded-md border-l-4 border-amber-500 bg-amber-50 dark:bg-amber-950/30 p-3"><div className="font-semibold">4. High-flow nasal cannula (HFNC)</div><div className="text-xs text-muted-foreground"><strong>{validWt ? `1-2 L/kg/min = ${hfLow}-${hfHigh} L/min` : '1-2 L/kg/min'}</strong> · heated + humidified · FiO₂ 30-100% titratable · generates ~2-5 cmH₂O PEEP. Reassess at 1-2 h.</div></div>
        <div className="rounded-md border-l-4 border-red-500 bg-red-50 dark:bg-red-950/30 p-3"><div className="font-semibold">5. Non-invasive (CPAP / BiPAP)</div><div className="text-xs text-muted-foreground">CPAP 5-10 cmH₂O · BiPAP IPAP 10-14 / EPAP 5. Needs cooperative patient, intact airway reflexes, no copious secretions.</div></div>
        <div className="rounded-md border-l-4 border-destructive bg-red-100 dark:bg-red-950/40 p-3"><div className="font-semibold">6. Intubate + mechanical ventilation</div><div className="text-xs text-muted-foreground">When NIV fails, airway compromised, apnea, or GCS ≤8. See Airway tab for RSI drugs.</div></div>
      </div>

      <h3 className="text-sm font-semibold mt-2">Bag-Valve-Mask (BVM)</h3>
      <div className="rounded-md bg-blue-50 dark:bg-blue-950/30 p-3 text-xs space-y-1">
        <div><strong>When:</strong> apnea, bradycardia (HR &lt;60 neonate; inadequate breathing at any age), during resuscitation.</div>
        <div><strong>Rate:</strong> Newborn 40-60/min · Infant-child 20-30/min · Adolescent 10-12/min (1 breath q5-6 sec).</div>
        <div><strong>Tidal volume:</strong> 6-8 mL/kg — gentle chest rise only. Avoid over-ventilation.</div>
        <div><strong>Technique:</strong> head tilt / jaw thrust, E-C or 2-thumb mask seal, squeeze 1 sec, release fully.</div>
        <div><strong>Not ventilating?</strong> MR SOPA — Mask reseal, Reposition airway, Suction, Open mouth, Pressure ↑, Alternative airway.</div>
      </div>

      <h3 className="text-sm font-semibold mt-2">Mechanical vent — starting settings</h3>
      <div className="rounded-md bg-muted/40 p-3 text-xs space-y-1">
        <div><strong>Mode:</strong> Volume-control OR Pressure-control. PRVC / SIMV-PS hybrids.</div>
        <div><strong>Tidal volume:</strong> <strong>{validWt ? `${tvLow}-${tvHigh} mL` : '6-8 mL/kg'}</strong> (6-8 mL/kg). Use 4-6 mL/kg for ARDS.</div>
        <div><strong>Rate:</strong> {rate ? `${rate}/min (age ${ageY} yr)` : 'Newborn 30-40 · Infant 25-35 · Child 16-20 · Adolescent 12-16'}.</div>
        <div><strong>PEEP:</strong> start 5 cmH₂O. Increase to 8-12+ for refractory hypoxia.</div>
        <div><strong>FiO₂:</strong> start 100%, wean rapidly to lowest that maintains target SpO₂.</div>
        <div><strong>I:E ratio:</strong> 1:2 normally; 1:3-4 for obstructive disease.</div>
        <div><strong>Plateau pressure:</strong> keep &lt;30 cmH₂O (ideally &lt;28).</div>
      </div>

      <h3 className="text-sm font-semibold mt-2">Adjusting for gas exchange</h3>
      <DrugTable notes>
        <Row name="Low SpO₂ (oxygenation)"         dose="↑ FiO₂"                  route="—" notes="Then ↑ PEEP (recruits collapsed alveoli)" />
        <Row name="↑ PCO₂ (ventilation)"            dose="↑ Rate"                  route="—" notes="Then ↑ Tidal volume" />
        <Row name="↓ PCO₂ (over-ventilating)"       dose="↓ Rate"                  route="—" notes="Then ↓ Tidal volume" />
        <Row name="High peak pressure"              dose="Check tube / compliance" route="—" notes="Suction, bronchodilator, lower TV" />
        <Row name="Auto-PEEP (asthma, bronch)"      dose="↓ Rate, ↑ Te"             route="—" notes="Disconnect + bag briefly if critical" />
      </DrugTable>
      <div className="rounded-md bg-green-50 dark:bg-green-950/30 p-3 text-xs text-green-900 dark:text-green-100">
        <strong>Mental model:</strong> Oxygenation is mostly <strong>FiO₂ + PEEP</strong>. Ventilation (CO₂) is mostly <strong>rate + tidal volume</strong>. Obstructive (asthma, bronchiolitis) → long expiratory time, permissive hypercapnia. Restrictive (ARDS) → low TV, high PEEP, permissive hypercapnia + hypoxia.
      </div>
      <div className="text-xs text-muted-foreground italic">AAP / PALS / AARC guidance.</div>
    </section>
  );
}

// ── Sepsis ──────────────────────────────────────────────────
export function SepsisPanel() {
  const [weight, setWeight] = useState('');
  const [age, setAge] = useState<'neonate' | 'infant' | 'child'>('child');
  const wt = Number.parseFloat(weight);
  const valid = Number.isFinite(wt) && wt > 0;
  const f = (perKg: number, max: number | null, unit = 'mg') => (valid ? formatDose(wt, perKg, max, unit) : null);
  const ageLbl = age === 'neonate' ? 'Neonate (0-28 d)' : age === 'infant' ? 'Young infant (29 d - 3 mo)' : 'Older child / adolescent';
  const bolus = valid ? Math.round(wt * 20) : null;

  return (
    <section className={card} data-testid="bedside-panel-sepsis">
      <h2 className="text-lg font-semibold">Sepsis &amp; Fever</h2>
      <div className="grid gap-2 grid-cols-1 sm:grid-cols-2 max-w-md">
        <div><label className={label}>Weight (kg)</label><input type="number" min="0.3" step="0.1" className={input} value={weight} onChange={(e) => setWeight(e.target.value)} data-testid="sepsis-weight" /></div>
        <div><label className={label}>Age band</label><select className={input} value={age} onChange={(e) => setAge(e.target.value as typeof age)} data-testid="sepsis-age"><option value="neonate">Neonate (0-28 d)</option><option value="infant">Infant (29 d - 3 mo)</option><option value="child">Older child / adolescent</option></select></div>
      </div>

      <div className="rounded-md border-2 border-destructive bg-red-50 dark:bg-red-950/30 p-3 text-sm font-semibold text-destructive">
        Sepsis approach — {ageLbl}{valid ? `, ${wt} kg` : ''}
      </div>

      <h3 className="text-sm font-semibold mt-2">Definition — Phoenix Sepsis Criteria (JAMA 2024)</h3>
      <div className="rounded-md bg-muted/40 p-3 text-xs space-y-1">
        <div><strong>Sepsis</strong> = suspected or confirmed infection + Phoenix Score ≥2 (organ dysfunction across respiratory, cardiovascular, coagulation, neurological).</div>
        <div><strong>Septic shock</strong> = sepsis + cardiovascular dysfunction (vasoactive support, or ↑lactate ≥5, or ↓MAP for age).</div>
        <div className="text-muted-foreground italic">Previous SIRS-based criteria (Goldstein 2005) are now superseded.</div>
      </div>

      <h3 className="text-sm font-semibold mt-2">Red flags</h3>
      <div className="rounded-md bg-red-50 dark:bg-red-950/30 p-3 text-xs text-red-900 dark:text-red-100">
        Abnormal behavior / mentation · Fever + ill-appearance · Tachycardia out of proportion to fever · Prolonged cap refill (&gt;3 s) · Cold/mottled extremities · Weak pulses or wide pulse pressure ("warm shock") · Hypotension is a <strong>LATE</strong> sign · Any immune compromise / indwelling line.
      </div>

      <h3 className="text-sm font-semibold mt-2">Empirical therapy — {ageLbl}</h3>
      <div className="rounded-md bg-muted/40 p-3 text-xs">
        {age === 'neonate' && <><strong>Workup (full sepsis eval):</strong> CBC+diff, CRP, blood culture, UA+urine culture (cath), <strong>LP</strong> (CSF+HSV PCR), CXR if respiratory sx, procalcitonin. <strong>Early-onset</strong> (&lt;72 h): GBS, E. coli, Listeria. <strong>Late-onset</strong> (&gt;72 h): CoNS, S. aureus, gram-negs, Candida.</>}
        {age === 'infant' && <><strong>Workup:</strong> Use validated rules — PECARN, Aronson, Rochester, Step-by-Step. CBC+ANC, procalcitonin/CRP, blood culture, UA+urine culture. Many warrant LP + admission + empiric abx. <strong>Coverage:</strong> GBS, E. coli, Listeria (up to ~6 wk), S. pneumo, N. meningitidis, H. flu, Salmonella.</>}
        {age === 'child' && <><strong>Recognition:</strong> Phoenix score or clinical concern + suspected infection. <strong>Workup:</strong> CBC, CRP, procalcitonin, blood cx (+site-specific), lactate, blood gas, glucose, electrolytes, coags, LP if CNS concern. Source-directed imaging.</>}
      </div>

      {valid && (
        <DrugTable>
          {age === 'neonate' && <>
            <Row name="Ampicillin"      dose={<Dose label={f(100, 2000)!.label} />}  route="IV" notes="q8-12h. Covers GBS, Listeria, Enterococcus." />
            <Row name="Gentamicin"      dose={<Dose label={f(4, 120)!.label} />}     route="IV" notes="q24-48h. Monitor levels." />
            <Row name="Cefotaxime (add)" dose={<Dose label={f(50, 2000)!.label} />} route="IV" notes="If meningitis or gram-neg concern." />
            <Row name="Acyclovir"        dose={<Dose label={f(20, 1200)!.label} />} route="IV q8h" notes="HSV risk: maternal lesions, vesicles, seizures, CSF pleocytosis." />
          </>}
          {age === 'infant' && <>
            <Row name="Ceftriaxone" dose={<Dose label={f(75, 2000)!.label} />}  route="IV / IM" notes="q24h (100 mg/kg/day divided q12h for meningitis). <strong>Avoid &lt;28 d</strong> if hyperbilirubinemia." />
            <Row name="Ampicillin"  dose={<Dose label={f(100, 2000)!.label} />} route="IV"      notes="If &lt;6 wk: add for Listeria coverage." />
            <Row name="Vancomycin"  dose={<Dose label={f(15, 1000)!.label} />}  route="IV"      notes="If severe / MRSA risk / meningitis." />
            <Row name="Acyclovir"    dose={<Dose label={f(20, 1200)!.label} />} route="IV q8h"  notes="&lt;6 wk with suspicion of HSV." />
          </>}
          {age === 'child' && <>
            <Row name="Ceftriaxone"            dose={<Dose label={f(50, 2000)!.label} />}  route="IV" notes="q24h (100 mg/kg/day divided for meningitis)." />
            <Row name="Vancomycin"             dose={<Dose label={f(15, 1000)!.label} />}  route="IV" notes="q6h. If severe, indwelling line, or MRSA prevalence &gt;10%." />
            <Row name="Piperacillin-tazobactam" dose={<Dose label={f(100, 4500)!.label} />} route="IV" notes="If intra-abdominal / neutropenic." />
            <Row name="Clindamycin"            dose={<Dose label={f(10, 900)!.label} />}   route="IV" notes="Adjunct for toxic shock syndrome (toxin suppression)." />
            <Row name="Acyclovir"              dose={<Dose label={f(20, 1200)!.label} />}  route="IV q8h" notes="If HSV CNS concern." />
          </>}
        </DrugTable>
      )}

      <h3 className="text-sm font-semibold mt-2">First-hour bundle (SSC Peds 2020)</h3>
      <div className="space-y-2 text-sm">
        <div className="rounded-md border-l-4 border-blue-500 bg-blue-50 dark:bg-blue-950/30 p-3"><div className="font-semibold">0-5 min — Recognize</div><div className="text-xs text-muted-foreground">Screen, sepsis huddle/activation, ABCs, O₂ to SpO₂ &gt;94%, warm.</div></div>
        <div className="rounded-md border-l-4 border-purple-500 bg-purple-50 dark:bg-purple-950/30 p-3"><div className="font-semibold">5-15 min — Access &amp; labs</div><div className="text-xs text-muted-foreground">Two IVs or IO. Draw blood cx (ideally before abx), lactate, CBC, CMP, coags, blood gas, glucose. UA + culture. Source-specific cultures.</div></div>
        <div className="rounded-md border-l-4 border-amber-500 bg-amber-50 dark:bg-amber-950/30 p-3"><div className="font-semibold">15-30 min — Fluids</div><div className="text-xs text-muted-foreground">{valid ? <>NS or LR <strong>{bolus} mL</strong> bolus (20 mL/kg) over 5-10 min.</> : <>NS/LR 10-20 mL/kg bolus over 5-10 min.</>} Reassess HR, perfusion, lungs, liver. Repeat up to 40-60 mL/kg; stop if crackles/hepatomegaly.</div></div>
        <div className="rounded-md border-l-4 border-green-500 bg-green-50 dark:bg-green-950/30 p-3"><div className="font-semibold">30-60 min — Antibiotics + reassess</div><div className="text-xs text-muted-foreground">Broad-spectrum empiric abx within 1 hour (≤1 h in septic shock). Recheck lactate, perfusion.</div></div>
        <div className="rounded-md border-l-4 border-destructive bg-red-50 dark:bg-red-950/30 p-3"><div className="font-semibold">&gt;60 min — Fluid-refractory shock</div><div className="text-xs text-muted-foreground">Start vasoactive (<strong>epinephrine 0.05-0.3 mcg/kg/min</strong> cold / <strong>norepinephrine 0.05-0.3 mcg/kg/min</strong> warm). Central/IO access. Stress-dose hydrocortisone {valid ? <><strong>{f(2, 100)!.value} mg</strong> IV (2 mg/kg, max 100 mg)</> : '2 mg/kg IV (max 100 mg)'} if catecholamine-resistant. ICU.</div></div>
      </div>

      <h3 className="text-sm font-semibold mt-2">Resuscitation targets</h3>
      <div className="rounded-md bg-muted/40 p-3 text-xs">
        Normal mentation · Cap refill ≤2 s · Warm extremities · Strong peripheral pulses · UOP ≥1 mL/kg/hr · MAP ≥5th %ile for age (&gt;65 mmHg adolescent) · SpO₂ ≥94% · Lactate trending down.
      </div>
      <div className="text-xs text-muted-foreground italic">Phoenix Sepsis Criteria (Schlapbach et al., JAMA 2024) · Surviving Sepsis Campaign Pediatric 2020 · AAP pediatric sepsis guidance.</div>
    </section>
  );
}

// ── Burns ───────────────────────────────────────────────────
// Lund-Browder age-adjusted region percentages ported VERBATIM from
// public/js/bedside/burns.js:10-30.
const LUND_BROWDER: Array<{ key: string; label: string; vals: [number, number, number, number, number]; ageSensitive?: boolean }> = [
  { key: 'head',       label: 'Head',             vals: [18, 13, 11, 9, 7],   ageSensitive: true },
  { key: 'neck',       label: 'Neck',             vals: [2, 2, 2, 2, 2] },
  { key: 'ant_trunk',  label: 'Anterior trunk',   vals: [13, 13, 13, 13, 13] },
  { key: 'post_trunk', label: 'Posterior trunk',  vals: [13, 13, 13, 13, 13] },
  { key: 'r_buttock',  label: 'Right buttock',    vals: [2.5, 2.5, 2.5, 2.5, 2.5] },
  { key: 'l_buttock',  label: 'Left buttock',     vals: [2.5, 2.5, 2.5, 2.5, 2.5] },
  { key: 'genital',    label: 'Genitalia',        vals: [1, 1, 1, 1, 1] },
  { key: 'r_uparm',    label: 'R upper arm',      vals: [4, 4, 4, 4, 4] },
  { key: 'l_uparm',    label: 'L upper arm',      vals: [4, 4, 4, 4, 4] },
  { key: 'r_forearm',  label: 'R forearm',        vals: [3, 3, 3, 3, 3] },
  { key: 'l_forearm',  label: 'L forearm',        vals: [3, 3, 3, 3, 3] },
  { key: 'r_hand',     label: 'R hand',           vals: [2.5, 2.5, 2.5, 2.5, 2.5] },
  { key: 'l_hand',     label: 'L hand',           vals: [2.5, 2.5, 2.5, 2.5, 2.5] },
  { key: 'r_thigh',    label: 'R thigh',          vals: [5.5, 8, 8.5, 9, 9.5], ageSensitive: true },
  { key: 'l_thigh',    label: 'L thigh',          vals: [5.5, 8, 8.5, 9, 9.5], ageSensitive: true },
  { key: 'r_leg',      label: 'R lower leg',      vals: [5, 5.5, 6, 6.5, 7],   ageSensitive: true },
  { key: 'l_leg',      label: 'L lower leg',      vals: [5, 5.5, 6, 6.5, 7],   ageSensitive: true },
  { key: 'r_foot',     label: 'R foot',           vals: [3.5, 3.5, 3.5, 3.5, 3.5] },
  { key: 'l_foot',     label: 'L foot',           vals: [3.5, 3.5, 3.5, 3.5, 3.5] },
];
const AGE_BANDS: Array<{ id: 'infant' | 'young' | 'child' | 'adol' | 'adult'; label: string }> = [
  { id: 'infant', label: 'Infant (<1 y)' },
  { id: 'young',  label: 'Young child (1-5 y)' },
  { id: 'child',  label: 'Child (5-10 y)' },
  { id: 'adol',   label: 'Adolescent (10-15 y)' },
  { id: 'adult',  label: 'Adult (>15 y)' },
];

export function BurnsPanel() {
  const [weight, setWeight] = useState('');
  const [ageBand, setAgeBand] = useState<'infant' | 'young' | 'child' | 'adol' | 'adult'>('young');
  const [override, setOverride] = useState('');
  const [pct, setPct] = useState<Record<string, number>>({});

  const ageIdx = AGE_BANDS.findIndex((a) => a.id === ageBand);
  const computedTbsa = LUND_BROWDER.reduce(
    (sum, r) => sum + r.vals[ageIdx] * Math.min(100, Math.max(0, pct[r.key] ?? 0)) / 100,
    0,
  );
  const tbsa = override.trim() ? Number(override) : Math.round(computedTbsa * 10) / 10;
  const wt = Number.parseFloat(weight);
  const validWt = Number.isFinite(wt) && wt > 0;
  const validTbsa = Number.isFinite(tbsa) && tbsa > 0;

  const total = validWt && validTbsa ? Math.round(4 * wt * tbsa) : 0;
  const first8 = Math.round(total / 2);
  const rateFirst = Math.round(first8 / 8);
  const next16 = total - first8;
  const rateNext = Math.round(next16 / 16);
  const maint = validWt
    ? Math.round(wt <= 10 ? wt * 4 : wt <= 20 ? 40 + (wt - 10) * 2 : 60 + (wt - 20))
    : 0;

  return (
    <section className={card} data-testid="bedside-panel-burns">
      <h2 className="text-lg font-semibold">Burns — Lund-Browder + Parkland</h2>
      <div className="grid gap-2 grid-cols-1 sm:grid-cols-3 max-w-xl">
        <div><label className={label}>Weight (kg)</label><input type="number" min="0.3" step="0.1" className={input} value={weight} onChange={(e) => setWeight(e.target.value)} data-testid="burn-weight" /></div>
        <div><label className={label}>Age band</label><select className={input} value={ageBand} onChange={(e) => setAgeBand(e.target.value as typeof ageBand)} data-testid="burn-age">{AGE_BANDS.map((a) => <option key={a.id} value={a.id}>{a.label}</option>)}</select></div>
        <div><label className={label}>TBSA override (%)</label><input type="number" min="0" max="100" step="1" className={input} value={override} onChange={(e) => setOverride(e.target.value)} placeholder={validTbsa ? String(tbsa) : 'auto'} data-testid="burn-tbsa" /></div>
      </div>

      <h3 className="text-sm font-semibold">Body parts — % of each region burned (2° or deeper)</h3>
      <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 gap-2">
        {LUND_BROWDER.map((r) => {
          const max = r.vals[ageIdx];
          return (
            <div key={r.key} className="flex items-center gap-2 bg-muted/30 rounded p-2">
              <label className="flex-1 text-xs">{r.label} <span className="text-muted-foreground">({max}%{r.ageSensitive ? '*' : ''})</span></label>
              <input type="number" min="0" max="100" step="5" className="w-16 rounded border border-input bg-background px-2 py-1 text-xs text-right" value={pct[r.key] ?? 0} onChange={(e) => setPct({ ...pct, [r.key]: Number(e.target.value) })} data-testid={'burn-region-' + r.key} />
              <span className="text-xs text-muted-foreground">%</span>
            </div>
          );
        })}
      </div>
      {validTbsa && <div className="text-sm text-muted-foreground">Computed TBSA: <strong>{tbsa}%</strong></div>}
      {!validWt && <p className="text-xs text-destructive">Enter weight (kg) to see Parkland + maintenance fluids.</p>}
      {!validTbsa && validWt && <p className="text-xs text-destructive">Enter % per region or override TBSA to compute fluids.</p>}

      {validWt && validTbsa && (
        <>
          <div className="rounded-md border-2 border-destructive bg-red-50 dark:bg-red-950/30 p-3 text-sm font-semibold text-destructive" data-testid="burn-result">
            Burn fluid resuscitation — {wt} kg, {tbsa}% TBSA (2° or deeper)
          </div>
          <div className="rounded-md bg-red-50 dark:bg-red-950/30 p-3 text-sm space-y-1">
            <div><strong>Parkland formula:</strong> 4 mL × kg × %TBSA = <strong>{total} mL LR over 24 hours</strong></div>
            <div><strong>First 8 h</strong> (from time of burn): {first8} mL (~<strong>{rateFirst} mL/hr</strong>)</div>
            <div><strong>Next 16 h:</strong> {next16} mL (~<strong>{rateNext} mL/hr</strong>)</div>
          </div>
          <div className="rounded-md bg-muted/40 p-3 text-sm">
            <strong>Plus maintenance (4-2-1):</strong> {maint} mL/hr (D5 ½NS ± 20 mEq KCl/L once UOP established). Consider dextrose in children &lt;30 kg.
          </div>
          <div className="rounded-md bg-muted/40 p-3 text-sm">
            <strong>Titrate to UOP:</strong> target 1-2 mL/kg/hr (infants / children), 0.5-1 mL/kg/hr (adolescents). <strong>Clinical response trumps formula.</strong>
          </div>
        </>
      )}

      <h3 className="text-sm font-semibold">Other pearls</h3>
      <div className="rounded-md bg-muted/40 p-3 text-xs space-y-1">
        <div><strong>Rule of palm:</strong> Patient's palm + fingers ≈ 1% TBSA — good for scattered burns.</div>
        <div><strong>First-degree burns DO NOT count</strong> toward TBSA or Parkland.</div>
        <div><strong>Analgesia:</strong> Morphine 0.05-0.1 mg/kg IV q2h, or fentanyl 1-2 mcg/kg IV q30-60 min.</div>
        <div><strong>Tetanus</strong> prophylaxis if indicated. Tdap/Td ± TIG.</div>
      </div>

      <h3 className="text-sm font-semibold">Burn center referral (ABA)</h3>
      <div className="rounded-md bg-amber-50 dark:bg-amber-950/30 p-3 text-xs text-amber-900 dark:text-amber-100">
        Partial-thickness &gt;10% TBSA · any full-thickness · face/hands/feet/genital/perineum/major joints · electrical/chemical/inhalation · associated trauma · significant comorbidities · pediatric burns in non-pediatric center.
      </div>
      <div className="text-xs text-muted-foreground italic">
        ABA Advanced Burn Life Support 2018 · Parkland formula: Baxter 1968 · Lund-Browder 1944.
      </div>
    </section>
  );
}
