// ============================================================
// PHYSICAL EXAM GUIDE — full React port.
//
// Renders:
//   • Age-group + system pills (6 × 4 = 24 combinations)
//   • System overview banner
//   • CV system extras: APTM legend, cardiac sounds, innocent murmurs
//   • Resp system extras: respiratory sounds library
//   • Collapsible grading-scales reference (system-scoped)
//   • Component checklist with per-step normal / abnormal / (unset)
//     toggle, abnormal-hints hint list, pearl + significance callouts
//   • Patient age / gender + model inputs
//   • Generate Exam Report → POST /api/generate-pe-narrative
//
// PE_DATA is the full hierarchy ported verbatim from vanilla
// peGuide.js (see client/src/data/pe-data.ts). Clinical reference
// libraries (scales, APTM, sound files) live in pe-guide.ts.
// ============================================================

import { useMemo, useState } from 'react';
import { useMutation } from '@tanstack/react-query';
import { api } from '@/lib/api';
import type { PeNarrativeOk } from '@/shared/types';
import {
  PE_DATA,
  AGE_GROUP_ORDER,
  SYSTEM_ORDER,
  SYSTEM_LABELS,
  type PeComponent,
  type PeStep,
} from '@/data/pe-data';
import {
  SCALES,
  SYSTEM_SCALES,
  APTM_LEGEND,
  INNOCENT_MURMURS,
  RESP_SOUNDS,
  CARDIAC_SOUNDS,
  type ScaleDef,
  type SoundEntry,
} from '@/data/pe-guide';

const card = 'rounded-lg border border-border bg-card p-5 space-y-3';
const pill = 'px-3 py-1.5 rounded-full text-xs font-medium border transition-colors cursor-pointer';
const btnPrimary = 'rounded-md bg-primary text-primary-foreground px-4 py-2 text-sm font-medium disabled:opacity-50';
const btnGhost = 'rounded-md border border-border bg-background px-3 py-2 text-sm font-medium hover:bg-muted disabled:opacity-50';
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';

type StepStatus = 'normal' | 'abnormal' | null;

// Key used to identify a step in the status map across age-group / system.
function stepKey(age: string, sys: string, componentIdx: number, stepIdx: number) {
  return `${age}/${sys}/${componentIdx}/${stepIdx}`;
}

function ScaleCard({ id, scale }: { id: string; scale: ScaleDef }) {
  return (
    <section className="rounded-md border border-border bg-background p-3" data-testid={'scale-' + id}>
      <h4 className="text-sm font-semibold mb-2">{scale.title}</h4>
      <table className="w-full text-xs">
        <tbody>
          {scale.rows.map(([labelText, desc], i) => (
            <tr key={i} className="border-b border-border last:border-0">
              <td className="py-1 pr-3 font-mono font-semibold whitespace-nowrap">{labelText}</td>
              <td className="py-1 text-muted-foreground">{desc}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </section>
  );
}

function SoundCard({ entry }: { entry: SoundEntry }) {
  return (
    <div className="rounded-md border border-border bg-background p-3 space-y-2" data-testid={'sound-' + entry.key}>
      <div className="text-sm font-semibold">{entry.title}</div>
      <audio controls preload="none" className="w-full">
        <source src={entry.src} />
      </audio>
      <div className="text-xs space-y-0.5 text-muted-foreground">
        <div><span className="font-semibold">Where:</span> {entry.where}</div>
        {entry.rate && <div><span className="font-semibold">Rate:</span> {entry.rate}</div>}
        <div><span className="font-semibold">Features:</span> {entry.features}</div>
        <div><span className="font-semibold">Clinical:</span> {entry.clinical}</div>
      </div>
    </div>
  );
}

function StepRow({
  step,
  status,
  onStatus,
}: {
  step: PeStep;
  status: StepStatus;
  onStatus: (next: StepStatus) => void;
}) {
  const base = 'text-xs font-medium px-2 py-1 rounded border';
  return (
    <div className="flex items-start gap-2 py-2 border-b border-border last:border-0">
      <div className="flex-1 min-w-0">
        <div className="text-sm font-medium">{step.label}</div>
        <div className="text-xs text-muted-foreground mt-0.5">
          <span className="font-semibold uppercase tracking-wide">Method:</span> {step.method}
        </div>
        <div className="text-xs text-muted-foreground">
          <span className="font-semibold uppercase tracking-wide">Normal:</span> {step.normal}
        </div>
      </div>
      <div className="flex flex-col sm:flex-row gap-1 flex-shrink-0">
        <button
          type="button"
          onClick={() => onStatus(status === 'normal' ? null : 'normal')}
          className={
            base + ' ' +
            (status === 'normal'
              ? 'bg-green-600 text-white border-green-600'
              : 'border-green-600 text-green-700 hover:bg-green-50 dark:hover:bg-green-950/30')
          }
        >
          Normal
        </button>
        <button
          type="button"
          onClick={() => onStatus(status === 'abnormal' ? null : 'abnormal')}
          className={
            base + ' ' +
            (status === 'abnormal'
              ? 'bg-destructive text-white border-destructive'
              : 'border-destructive text-destructive hover:bg-red-50 dark:hover:bg-red-950/30')
          }
        >
          Abnormal
        </button>
      </div>
    </div>
  );
}

function ComponentCard({
  age,
  sys,
  idx,
  comp,
  getStatus,
  setStatus,
}: {
  age: string;
  sys: string;
  idx: number;
  comp: PeComponent;
  getStatus: (k: string) => StepStatus;
  setStatus: (k: string, next: StepStatus) => void;
}) {
  return (
    <div className={card} data-testid={`pe-component-${age}-${sys}-${idx}`}>
      <h3 className="text-base font-semibold">{comp.name}</h3>
      <div>
        {comp.steps.map((step, si) => {
          const k = stepKey(age, sys, idx, si);
          return (
            <StepRow
              key={si}
              step={step}
              status={getStatus(k)}
              onStatus={(next) => setStatus(k, next)}
            />
          );
        })}
      </div>
      {comp.abnormalHints.length > 0 && (
        <div className="rounded-md bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900 p-3">
          <div className="text-xs font-semibold uppercase tracking-wide text-destructive mb-1">Watch for</div>
          <ul className="list-disc pl-5 text-xs text-red-900 dark:text-red-200 space-y-0.5">
            {comp.abnormalHints.map((h, hi) => <li key={hi}>{h}</li>)}
          </ul>
        </div>
      )}
      {comp.pearl && (
        <div className="rounded-md bg-amber-50 dark:bg-amber-950/30 border border-amber-300 dark:border-amber-800 p-3 text-xs text-amber-900 dark:text-amber-100">
          <span className="font-semibold uppercase tracking-wide">Pearl:</span> {comp.pearl}
        </div>
      )}
      {comp.significance && (
        <div className="rounded-md bg-sky-50 dark:bg-sky-950/30 border border-sky-200 dark:border-sky-900 p-3 text-xs text-sky-900 dark:text-sky-100">
          <span className="font-semibold uppercase tracking-wide">Significance:</span> {comp.significance}
        </div>
      )}
    </div>
  );
}

export default function PeGuide() {
  const [age, setAge] = useState<(typeof AGE_GROUP_ORDER)[number]>('toddler');
  const [sys, setSys] = useState<(typeof SYSTEM_ORDER)[number]>('msk');
  const [patientAge, setPatientAge] = useState('');
  const [patientGender, setPatientGender] = useState('');
  const [format, setFormat] = useState<'narrative' | 'list'>('narrative');
  const [statusMap, setStatusMap] = useState<Record<string, StepStatus>>({});
  const [narrative, setNarrative] = useState<string | null>(null);

  const group = PE_DATA[age];
  const section = group[sys];

  const generate = useMutation({
    mutationFn: (body: unknown) => api.post<PeNarrativeOk>('/api/generate-pe-narrative', body),
    onSuccess: (data) => setNarrative(data.narrative),
    onError: (e: Error) => setNarrative('Generation failed: ' + e.message),
  });

  const summary = useMemo(() => {
    let normal = 0, abnormal = 0, notAssessed = 0;
    section.components.forEach((c, ci) =>
      c.steps.forEach((_, si) => {
        const k = stepKey(age, sys, ci, si);
        const v = statusMap[k] ?? null;
        if (v === 'normal') normal++;
        else if (v === 'abnormal') abnormal++;
        else notAssessed++;
      }),
    );
    return { normal, abnormal, notAssessed };
  }, [age, sys, section, statusMap]);

  function reset() {
    // Only clear the current system's entries, not all state.
    setStatusMap((prev) => {
      const next = { ...prev };
      section.components.forEach((c, ci) =>
        c.steps.forEach((_, si) => { delete next[stepKey(age, sys, ci, si)]; }),
      );
      return next;
    });
    setNarrative(null);
  }

  function setAllNormal() {
    setStatusMap((prev) => {
      const next = { ...prev };
      section.components.forEach((c, ci) =>
        c.steps.forEach((_, si) => { next[stepKey(age, sys, ci, si)] = 'normal'; }),
      );
      return next;
    });
  }

  function onGenerate() {
    setNarrative(null);
    const steps: Array<{ component: string; label: string; method: string; normal: string; status: StepStatus; note?: string }> = [];
    section.components.forEach((c, ci) =>
      c.steps.forEach((st, si) => {
        steps.push({
          component: c.name,
          label: st.label,
          method: st.method,
          normal: st.normal,
          status: statusMap[stepKey(age, sys, ci, si)] ?? null,
        });
      }),
    );
    generate.mutate({
      steps,
      ageGroup: age,
      system: sys,
      patientAge: patientAge || undefined,
      patientGender: patientGender || undefined,
      format,
    });
  }

  const totalAssessed = summary.normal + summary.abnormal;

  return (
    <div className="max-w-5xl mx-auto p-6 space-y-5">
      <header>
        <h1 className="text-2xl font-semibold">Physical Exam Guide</h1>
        <p className="text-sm text-muted-foreground">
          Age-group and system-specific exam checklist with abnormal-finding hints. Toggle normal / abnormal
          on each step, then generate a narrative for your note.
        </p>
      </header>

      {/* Age-group pills */}
      <div className="space-y-2">
        <div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">Age group</div>
        <div className="flex flex-wrap gap-2" data-testid="pe-age-group-pills">
          {AGE_GROUP_ORDER.map((g) => (
            <button
              key={g}
              type="button"
              onClick={() => setAge(g)}
              className={pill + (age === g ? ' bg-primary text-primary-foreground border-primary' : ' bg-muted hover:bg-muted/80 border-border')}
              data-testid={'pe-age-' + g}
            >
              {PE_DATA[g].label}
            </button>
          ))}
        </div>
      </div>

      {/* System pills */}
      <div className="space-y-2">
        <div className="text-xs font-semibold text-muted-foreground uppercase tracking-wide">System</div>
        <div className="flex flex-wrap gap-2" data-testid="pe-system-pills">
          {SYSTEM_ORDER.map((s) => (
            <button
              key={s}
              type="button"
              onClick={() => setSys(s)}
              className={pill + (sys === s ? ' bg-primary text-primary-foreground border-primary' : ' bg-muted hover:bg-muted/80 border-border')}
              data-testid={'pe-system-' + s}
            >
              {SYSTEM_LABELS[s]}
            </button>
          ))}
        </div>
      </div>

      {/* Overview */}
      <section className={card + ' border-l-4 border-l-primary'} data-testid="pe-overview">
        <h2 className="text-lg font-semibold">{group.label} — {SYSTEM_LABELS[sys]}</h2>
        <p className="text-sm text-muted-foreground">{section.overview}</p>
      </section>

      {/* System-specific references */}
      {sys === 'cv' && (
        <section className={card} data-testid="pe-cv-aptm">
          <h3 className="text-base font-semibold">Auscultation landmarks (APTM + Erb's)</h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
            {APTM_LEGEND.map((p) => (
              <div key={p.letter} className="flex gap-3 items-start rounded-md border border-border p-3">
                <div
                  className="w-8 h-8 rounded-full flex items-center justify-center font-bold text-white flex-shrink-0"
                  style={{ background: p.color }}
                >
                  {p.letter}
                </div>
                <div className="min-w-0 text-sm">
                  <div className="font-semibold">{p.title}</div>
                  <div className="text-xs text-muted-foreground">{p.location}</div>
                  <div className="text-xs mt-1"><strong>Listen for:</strong> {p.listen}</div>
                  {p.innocent && <div className="text-xs text-green-700 dark:text-green-300 mt-1"><em>Innocent:</em> {p.innocent}</div>}
                </div>
              </div>
            ))}
          </div>
          <h3 className="text-base font-semibold mt-3">Cardiac sounds library</h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
            {CARDIAC_SOUNDS.map((s) => <SoundCard key={s.key} entry={s} />)}
          </div>
          <h3 className="text-base font-semibold mt-3">Classic innocent murmurs</h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
            {INNOCENT_MURMURS.map((m) => (
              <div key={m.name} className="rounded-md border border-green-200 dark:border-green-900 bg-green-50 dark:bg-green-950/30 p-3 text-sm space-y-1">
                <div className="font-semibold">{m.name}</div>
                <div className="text-xs text-muted-foreground">Age: {m.age} · Location: {m.location}</div>
                <div className="text-xs"><strong>Sound:</strong> {m.character}</div>
                <div className="text-xs"><strong>Confirm innocent:</strong> {m.confirm}</div>
              </div>
            ))}
          </div>
        </section>
      )}

      {sys === 'resp' && (
        <section className={card} data-testid="pe-resp-sounds">
          <h3 className="text-base font-semibold">Respiratory sounds library</h3>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3">
            {RESP_SOUNDS.map((s) => <SoundCard key={s.key} entry={s} />)}
          </div>
        </section>
      )}

      {/* Grading scales (system-scoped, collapsible) */}
      {SYSTEM_SCALES[sys] && SYSTEM_SCALES[sys].length > 0 && (
        <details className={card} data-testid="pe-scales">
          <summary className="cursor-pointer font-semibold text-sm">Grading scales &amp; reference</summary>
          <div className="grid grid-cols-1 md:grid-cols-2 gap-3 mt-3">
            {SYSTEM_SCALES[sys].map((sk: string) => {
              const scale = SCALES[sk];
              if (!scale) return null;
              return <ScaleCard key={sk} id={sk} scale={scale} />;
            })}
          </div>
        </details>
      )}

      {/* Checklist */}
      <section className="space-y-3" data-testid="pe-checklist">
        <div className="flex flex-wrap items-center justify-between gap-2">
          <h2 className="text-lg font-semibold">Exam checklist</h2>
          <div className="text-xs text-muted-foreground flex items-center gap-3">
            <span className="text-green-600">{summary.normal} normal</span>
            <span className="text-destructive">{summary.abnormal} abnormal</span>
            <span>{summary.notAssessed} not assessed</span>
          </div>
        </div>
        <div className="flex flex-wrap gap-2">
          <button type="button" onClick={setAllNormal} className={btnGhost} data-testid="btn-pe-all-normal">
            Mark all normal
          </button>
          <button type="button" onClick={reset} className={btnGhost} data-testid="btn-pe-reset">
            Reset
          </button>
        </div>
        <div className="grid grid-cols-1 gap-3">
          {section.components.map((c, ci) => (
            <ComponentCard
              key={ci}
              age={age}
              sys={sys}
              idx={ci}
              comp={c}
              getStatus={(k) => statusMap[k] ?? null}
              setStatus={(k, next) => setStatusMap((prev) => ({ ...prev, [k]: next }))}
            />
          ))}
        </div>
      </section>

      {/* Generate narrative */}
      <section className={card} data-testid="pe-generate">
        <h2 className="text-lg font-semibold">Generate Exam Report</h2>
        <p className="text-sm text-muted-foreground">Uses the statuses above + optional patient context.</p>
        <div className="grid grid-cols-1 md:grid-cols-3 gap-3">
          <input
            className={input}
            placeholder="Patient age (e.g. 3y)"
            value={patientAge}
            onChange={(e) => setPatientAge(e.target.value)}
          />
          <input
            className={input}
            placeholder="Patient gender (optional)"
            value={patientGender}
            onChange={(e) => setPatientGender(e.target.value)}
          />
          <select
            className={input}
            value={format}
            onChange={(e) => setFormat(e.target.value as 'narrative' | 'list')}
          >
            <option value="narrative">Narrative</option>
            <option value="list">List</option>
          </select>
        </div>
        <div className="flex items-center gap-3">
          <button
            type="button"
            className={btnPrimary}
            onClick={onGenerate}
            disabled={generate.isPending || totalAssessed === 0}
            data-testid="btn-pe-generate"
          >
            {generate.isPending ? 'Generating…' : 'Generate Exam Report'}
          </button>
          {totalAssessed === 0 && (
            <span className="text-xs text-muted-foreground">Mark at least one step before generating.</span>
          )}
        </div>
        {narrative && (
          <div className="rounded-md border border-border bg-muted/40 p-3 whitespace-pre-wrap text-sm" data-testid="pe-narrative">
            {narrative}
          </div>
        )}
      </section>
    </div>
  );
}
