// PatientDashboard.jsx – Hero screen with 4 panels
const { useState, useMemo, useEffect } = React;

// ── Generate a realistic time-series for any metric, seeded by patient + field ─
function synthSeries(patient, field, unit) {
  // Deterministic pseudo-random from patient id + field
  let seed = 0; const s = (patient.id + field);
  for (let i=0;i<s.length;i++) seed = (seed * 31 + s.charCodeAt(i)) | 0;
  const rand = () => { seed = (seed * 1103515245 + 12345) & 0x7fffffff; return (seed / 0x7fffffff); };

  // Pick a trajectory shape from the actual patient signal
  const egfr = patient.egfrTrend || [];
  const anchors = {
    'eGFR':          egfr.length ? egfr.map(p=>p.v) : [60,58,55,52,50,48,45],
    'Creatinine':    [patient.creatinine*0.82, patient.creatinine*0.88, patient.creatinine*0.94, patient.creatinine, patient.creatinine*1.02, patient.creatinine*1.05],
    'Cystatin C':    [patient.cystatinC*0.85, patient.cystatinC*0.92, patient.cystatinC*0.97, patient.cystatinC, patient.cystatinC*1.03],
    'Urine albumin/cr': [Math.max(5,patient.urineAlbumin-20), Math.max(5,patient.urineAlbumin-12), patient.urineAlbumin-6, patient.urineAlbumin, patient.urineAlbumin+4],
    'Blood pressure (systolic)': (() => { const b = parseInt(String(patient.bp).split('/')[0])||130; return [b-8, b-4, b-6, b-2, b, b+2, b-3]; })(),
    'htTKV':         (patient.tkvTrend||[]).map(p=>p.v),
    'Annual TKV growth': [3.2, 4.1, 4.8, 5.4, 6.0, 6.2],
  };
  const baseArr = anchors[field] || [patient.egfr || 50];

  // Expand to ~24 measurement points over ~4 years with noise
  const N = 24;
  const series = [];
  const startDate = new Date('2022-01-15');
  for (let i=0;i<N;i++) {
    const t = i/(N-1);
    const idxF = t * (baseArr.length-1);
    const i0 = Math.floor(idxF), i1 = Math.min(baseArr.length-1, i0+1);
    const base = baseArr[i0] + (baseArr[i1]-baseArr[i0]) * (idxF-i0);
    const noise = (rand()-0.5) * (base * 0.04);
    const d = new Date(startDate.getTime() + i * (1000*60*60*24*62)); // ~62 days between points
    series.push({
      date: d,
      dateStr: d.toISOString().slice(0,10),
      v: Math.round((base + noise) * 100) / 100,
    });
  }
  // Ensure last point equals "current" for anchored fields
  if (field === 'eGFR' && patient.egfr) series[series.length-1].v = patient.egfr;
  if (field === 'Creatinine') series[series.length-1].v = patient.creatinine;
  if (field === 'Cystatin C') series[series.length-1].v = patient.cystatinC;
  if (field === 'Urine albumin/cr') series[series.length-1].v = patient.urineAlbumin;
  if (field === 'htTKV') series[series.length-1].v = patient.htTKV;
  return series;
}

// ── Filterable history chart for the provenance popover ───────────────────────
function HistoryChart({ field, unit, series, refLines=[], color='#D32027' }) {
  const RANGES = [
    { id:'1y', label:'1 Y',  months:12 },
    { id:'2y', label:'2 Y',  months:24 },
    { id:'5y', label:'5 Y',  months:60 },
    { id:'all', label:'All', months:Infinity },
  ];
  const [rangeId, setRangeId] = useState('all');
  const [hoverIdx, setHoverIdx] = useState(null);

  const filtered = useMemo(() => {
    const r = RANGES.find(r=>r.id===rangeId);
    if (r.months === Infinity) return series;
    const cutoff = new Date(series[series.length-1].date.getTime() - r.months * 30 * 24*60*60*1000);
    return series.filter(p => p.date >= cutoff);
  }, [series, rangeId]);

  const W = 420, H = 160, PAD = { t:16, r:16, b:34, l:40 };
  const innerW = W - PAD.l - PAD.r, innerH = H - PAD.t - PAD.b;
  const vals = filtered.map(p=>p.v);
  const vMin = Math.min(...vals, ...refLines.map(r=>r.v));
  const vMax = Math.max(...vals, ...refLines.map(r=>r.v));
  const pad = (vMax - vMin) * 0.15 || 1;
  const yMin = Math.max(0, vMin - pad), yMax = vMax + pad;

  const tMin = filtered[0].date.getTime(), tMax = filtered[filtered.length-1].date.getTime();
  const cx = (d) => PAD.l + ((d.getTime()-tMin)/((tMax-tMin)||1)) * innerW;
  const cy = (v) => PAD.t + innerH - ((v-yMin)/((yMax-yMin)||1)) * innerH;

  const pts = filtered.map(p => `${cx(p.date).toFixed(1)},${cy(p.v).toFixed(1)}`).join(' ');

  // Stats
  const first = filtered[0].v, last = filtered[filtered.length-1].v;
  const delta = last - first, pct = first ? ((delta/first)*100) : 0;

  // Y ticks
  const yTicks = [yMin, (yMin+yMax)/2, yMax].map(v => Math.round(v*100)/100);

  return (
    <div>
      <div style={{display:'flex', alignItems:'baseline', justifyContent:'space-between', marginBottom:8, flexWrap:'wrap', gap:8}}>
        <div>
          <div style={{fontSize:20, fontWeight:700, color:'#1A1A1A', lineHeight:1}}>
            {last}<span style={{fontSize:11, fontWeight:400, color:'#4A4A4A', marginLeft:4}}>{unit}</span>
          </div>
          <div style={{fontSize:11, color: delta>=0?'#D32027':'#2E7D5B', marginTop:3, fontWeight:600}}>
            {delta>=0?'▲':'▼'} {Math.abs(delta).toFixed(2)} {unit} ({pct>=0?'+':''}{pct.toFixed(1)}%) · {filtered.length} measurement{filtered.length===1?'':'s'}
          </div>
        </div>
        <div style={{display:'flex', gap:3, border:'1px solid #E5E5E7', borderRadius:3, padding:2, background:'#F7F7F8'}}>
          {RANGES.map(r => (
            <button key={r.id} onClick={()=>setRangeId(r.id)} style={{
              padding:'3px 9px', border:'none', borderRadius:2, fontSize:10, fontWeight:600, cursor:'pointer',
              background: rangeId===r.id ? '#1A1A1A' : 'transparent',
              color:    rangeId===r.id ? '#fff' : '#4A4A4A',
            }}>{r.label}</button>
          ))}
        </div>
      </div>
      <svg width="100%" viewBox={`0 0 ${W} ${H}`} style={{display:'block', overflow:'visible', background:'#FAFAFA', borderRadius:4, border:'1px solid #E5E5E7'}}
        onMouseLeave={()=>setHoverIdx(null)}>
        {/* Grid */}
        {yTicks.map(v => (
          <g key={v}>
            <line x1={PAD.l} x2={W-PAD.r} y1={cy(v)} y2={cy(v)} stroke="#EEEEEE" strokeWidth={1}/>
            <text x={PAD.l-4} y={cy(v)+3} textAnchor="end" fontSize={9} fill="#AEAEB2">{v}</text>
          </g>
        ))}
        {/* Reference lines */}
        {refLines.map((r,i) => (
          <g key={i}>
            <line x1={PAD.l} x2={W-PAD.r} y1={cy(r.v)} y2={cy(r.v)} stroke={r.color||'#C77700'} strokeDasharray="4 3" strokeWidth={1}/>
            <text x={W-PAD.r-2} y={cy(r.v)-3} textAnchor="end" fontSize={9} fill={r.color||'#C77700'} fontWeight={600}>{r.label}</text>
          </g>
        ))}
        {/* Line */}
        <polyline points={pts} fill="none" stroke={color} strokeWidth={2} strokeLinecap="round" strokeLinejoin="round"/>
        {/* Dots = real measurements */}
        {filtered.map((p,i) => {
          const isHover = hoverIdx===i;
          return (
            <g key={i}>
              <circle cx={cx(p.date)} cy={cy(p.v)} r={isHover?5:3.2} fill={color} stroke="#fff" strokeWidth={1.5}/>
              {/* Tick mark on X axis */}
              <line x1={cx(p.date)} x2={cx(p.date)} y1={H-PAD.b} y2={H-PAD.b+4} stroke="#4A4A4A" strokeWidth={1}/>
              {/* Invisible hover hit */}
              <circle cx={cx(p.date)} cy={cy(p.v)} r={10} fill="transparent"
                onMouseEnter={()=>setHoverIdx(i)}/>
            </g>
          );
        })}
        {/* X axis */}
        <line x1={PAD.l} x2={W-PAD.r} y1={H-PAD.b} y2={H-PAD.b} stroke="#AEAEB2" strokeWidth={1}/>
        {/* X axis year labels (first, middle, last) */}
        {(() => {
          const marks = [filtered[0], filtered[Math.floor(filtered.length/2)], filtered[filtered.length-1]];
          return marks.map((m,i) => (
            <text key={i} x={cx(m.date)} y={H-PAD.b+18} textAnchor={i===0?'start':i===2?'end':'middle'} fontSize={9} fill="#4A4A4A">
              {m.date.toLocaleDateString('en-GB',{month:'short',year:'numeric'})}
            </text>
          ));
        })()}
        {/* Hover tooltip */}
        {hoverIdx!==null && filtered[hoverIdx] && (() => {
          const p = filtered[hoverIdx];
          const tx = Math.min(W-PAD.r-110, Math.max(PAD.l, cx(p.date)+8));
          const ty = Math.max(PAD.t, cy(p.v)-38);
          return (
            <g pointerEvents="none">
              <rect x={tx} y={ty} width={108} height={32} rx={3} fill="#1A1A1A"/>
              <text x={tx+6} y={ty+13} fontSize={10} fill="#fff" fontWeight={600}>{p.v} {unit}</text>
              <text x={tx+6} y={ty+25} fontSize={9} fill="#AEAEB2">{p.date.toLocaleDateString('en-GB')}</text>
            </g>
          );
        })()}
      </svg>
      <div style={{marginTop:6, fontSize:10, color:'#AEAEB2', display:'flex', gap:8, alignItems:'center'}}>
        <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
          <span style={{width:8,height:8,borderRadius:'50%',background:color,display:'inline-block'}}/> Real measurement
        </span>
        {refLines.length>0 && <span style={{display:'inline-flex',alignItems:'center',gap:4}}>
          <span style={{width:14,height:0,borderTop:`1.5px dashed ${refLines[0].color||'#C77700'}`,display:'inline-block'}}/> Reference
        </span>}
        <span style={{marginLeft:'auto'}}>Tick marks on X axis = measurement dates</span>
      </div>
    </div>
  );
}

// Pure-SVG chart helpers — no external dependencies

function SvgLineChart({ data, dataKey='v', xKey='yr', width=400, height=130,
  color='#D32027', fill=null, refLines=[], label='', unit='' }) {
  const vals = data.map(d => d[dataKey]);
  const yMin = 0, yMax = Math.ceil(Math.max(...vals) * 1.2 / 10) * 10;
  const PAD = { t:8, r:48, b:24, l:32 };
  const W = width - PAD.l - PAD.r;
  const H = height - PAD.t - PAD.b;

  const cx = (i) => PAD.l + (i / (data.length - 1)) * W;
  const cy = (v) => PAD.t + H - ((v - yMin) / (yMax - yMin)) * H;

  const pts = data.map((d, i) => `${cx(i).toFixed(1)},${cy(d[dataKey]).toFixed(1)}`).join(' ');
  const fillPts = fill
    ? `${cx(0).toFixed(1)},${(PAD.t+H).toFixed(1)} ${pts} ${cx(data.length-1).toFixed(1)},${(PAD.t+H).toFixed(1)}`
    : null;

  // Y axis ticks
  const yTicks = [yMin, Math.round((yMin+yMax)/2), yMax];

  return (
    <svg width="100%" viewBox={`0 0 ${width} ${height}`} style={{display:'block', overflow:'visible'}}>
      {/* Grid lines */}
      {yTicks.map(v => (
        <line key={v} x1={PAD.l} x2={PAD.l+W} y1={cy(v)} y2={cy(v)}
          stroke="#F0F0F0" strokeWidth={1}/>
      ))}
      {/* Reference lines */}
      {refLines.map((rl, i) => (
        <g key={i}>
          <line x1={PAD.l} x2={PAD.l+W} y1={cy(rl.v)} y2={cy(rl.v)}
            stroke={rl.color||'#C77700'} strokeWidth={1} strokeDasharray="4 3"/>
          <text x={PAD.l+W+3} y={cy(rl.v)+3} fontSize={8} fill={rl.color||'#C77700'}>{rl.label}</text>
        </g>
      ))}
      {/* Fill area */}
      {fill && fillPts && (
        <polygon points={fillPts} fill={fill} opacity={0.12}/>
      )}
      {/* Line */}
      <polyline points={pts} fill="none" stroke={color} strokeWidth={2}
        strokeLinecap="round" strokeLinejoin="round"/>
      {/* Dots */}
      {data.map((d, i) => (
        <circle key={i} cx={cx(i)} cy={cy(d[dataKey])} r={3} fill={color} stroke="#fff" strokeWidth={1}/>
      ))}
      {/* X axis labels */}
      {data.map((d, i) => (
        <text key={i} x={cx(i)} y={height-4} textAnchor="middle" fontSize={9} fill="#4A4A4A">{d[xKey]}</text>
      ))}
      {/* Y axis labels */}
      {yTicks.map(v => (
        <text key={v} x={PAD.l-4} y={cy(v)+3} textAnchor="end" fontSize={9} fill="#4A4A4A">{v}</text>
      ))}
    </svg>
  );
}

function getRC() { return {}; }
function ChartFallback({ label }) { return null; }

const MAYO_BANDS = [
  { class:'1A', min:0, max:20, color:'#6B9E78' },
  { class:'1B', min:20, max:40, color:'#4A8C6F' },
  { class:'1C', min:40, max:60, color:'#C77700' },
  { class:'1D', min:60, max:80, color:'#D06020' },
  { class:'1E', min:80, max:100, color:'#D32027' },
];

const systemColor = { KIS:'#5B7FA6', LIS:'#7A6FA6', PACS:'#A67A4A', DIZ:'#4A8C6F' };

// Field → unit + reference line config for history chart
const FIELD_CONFIG = {
  'eGFR':                      { unit:'ml/min/1.73m²', refLines:[{v:60,color:'#C77700',label:'G3'},{v:30,color:'#D32027',label:'G4'},{v:25,color:'#D32027',label:'min'}] },
  'Creatinine':                { unit:'mg/dl',         refLines:[{v:1.2,color:'#C77700',label:'upper normal'}] },
  'Cystatin C':                { unit:'mg/L',          refLines:[{v:1.0,color:'#C77700',label:'upper normal'}] },
  'Urine albumin/cr':          { unit:'mg/g',          refLines:[{v:30,color:'#C77700',label:'A2'},{v:300,color:'#D32027',label:'A3'}] },
  'Blood pressure (systolic)': { unit:'mmHg',          refLines:[{v:130,color:'#C77700',label:'target'},{v:140,color:'#D32027',label:'stage 2'}] },
  'htTKV':                     { unit:'ml/m',          refLines:[{v:600,color:'#C77700',label:'Mayo 1C'}] },
  'Annual TKV growth':         { unit:'%/yr',          refLines:[{v:5,color:'#D32027',label:'rapid'}] },
};

function ProvenancePopover({ field, source, fhir, system, loinc, value, onClose, patient }) {
  const cfg = FIELD_CONFIG[field];
  const series = (cfg && patient) ? synthSeries(patient, field, cfg.unit) : null;

  return (
    <div style={{
      position:'fixed', inset:0, zIndex:200, display:'flex', alignItems:'center', justifyContent:'center',
      background:'rgba(26,26,26,0.28)'
    }} onClick={onClose}>
      <div style={{
        background:'#fff', border:'1px solid #E5E5E7', borderRadius:6, padding:20, maxWidth: series?560:420, width:'90%',
        maxHeight:'90vh', overflowY:'auto',
        boxShadow:'0 4px 24px rgba(0,0,0,0.10)'
      }} onClick={e=>e.stopPropagation()}>
        <div style={{display:'flex', justifyContent:'space-between', alignItems:'flex-start', marginBottom:14}}>
          <div>
            <div style={{fontSize:11, fontWeight:600, color:'#4A4A4A', textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:4}}>
              {series ? 'Measurement History & Provenance' : 'Data Provenance'}
            </div>
            <div style={{fontSize:16, fontWeight:700, color:'#1A1A1A'}}>{field}</div>
          </div>
          <button onClick={onClose} style={{background:'none',border:'none',cursor:'pointer',fontSize:18,color:'#4A4A4A',lineHeight:1}}>×</button>
        </div>

        {series && (
          <div style={{marginBottom:16}}>
            <HistoryChart field={field} unit={cfg.unit} series={series} refLines={cfg.refLines||[]}/>
          </div>
        )}

        <div style={{fontSize:10, fontWeight:700, color:'#AEAEB2', textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:6}}>Provenance</div>
        <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:'8px 16px', fontSize:12}}>
          {[
            ['Current value', value],
            ['Source system', source],
            ['FHIR resource', fhir],
            ['Subsystem', system],
            ['Code (LOINC/SNOMED)', loinc],
          ].map(([k,v]) => (
            <div key={k} style={{borderBottom:'1px solid #E5E5E7', paddingBottom:6}}>
              <div style={{color:'#4A4A4A', marginBottom:2}}>{k}</div>
              <div style={{color:'#1A1A1A', fontWeight:500, fontFamily: k==='FHIR resource'?'monospace':'inherit', fontSize: k==='FHIR resource'?11:12}}>{v}</div>
            </div>
          ))}
          <div style={{borderBottom:'1px solid #E5E5E7', paddingBottom:6}}>
            <div style={{color:'#4A4A4A', marginBottom:2}}>Consent</div>
            <div style={{color:'#2E7D5B', fontWeight:600, fontSize:12}}>✓ Broad MII</div>
          </div>
        </div>
      </div>
    </div>
  );
}

function Panel({ title, subtitle, children, style }) {
  return (
    <div style={{
      background:'#fff', border:'1px solid #E5E5E7', borderRadius:5,
      display:'flex', flexDirection:'column', overflow:'hidden', ...style
    }}>
      <div style={{padding:'12px 16px', borderBottom:'1px solid #E5E5E7', background:'#FAFAFA'}}>
        <div style={{fontSize:12, fontWeight:700, color:'#1A1A1A', textTransform:'uppercase', letterSpacing:'0.06em'}}>{title}</div>
        {subtitle && <div style={{fontSize:11, color:'#4A4A4A', marginTop:1}}>{subtitle}</div>}
      </div>
      <div style={{flex:1, padding:16, overflow:'auto'}}>{children}</div>
    </div>
  );
}

function DataPoint({ label, value, unit, onClick, color }) {
  return (
    <div onClick={onClick} style={{
      cursor: onClick ? 'pointer' : 'default',
      padding:'8px 10px', borderRadius:4, background:'#F7F7F8', border:'1px solid #E5E5E7',
      transition:'border-color 0.15s'
    }}
    onMouseEnter={e=>{ if(onClick) e.currentTarget.style.borderColor='#D32027'; }}
    onMouseLeave={e=>{ e.currentTarget.style.borderColor='#E5E5E7'; }}>
      <div style={{fontSize:10, color:'#4A4A4A', fontWeight:600, textTransform:'uppercase', letterSpacing:'0.05em', marginBottom:3}}>{label}</div>
      <div style={{fontSize:17, fontWeight:700, color: color||'#1A1A1A', lineHeight:1}}>
        {value} <span style={{fontSize:11, fontWeight:400, color:'#4A4A4A'}}>{unit}</span>
      </div>
      {onClick && <div style={{fontSize:10, color:'#D32027', marginTop:3}}>· provenance</div>}
    </div>
  );
}

function MayoBandChart({ mayoClass, htTKV }) {
  const classIndex = ['1A','1B','1C','1D','1E'].indexOf(mayoClass);
  const pct = classIndex * 20 + 10; // center of band
  return (
    <div>
      <div style={{marginBottom:8, fontSize:12, color:'#4A4A4A'}}>Mayo Imaging Classification – patient position</div>
      <div style={{position:'relative', height:32, borderRadius:4, overflow:'hidden', display:'flex'}}>
        {MAYO_BANDS.map(b => (
          <div key={b.class} style={{flex:1, background:b.color, display:'flex', alignItems:'center', justifyContent:'center'}}>
            <span style={{fontSize:10, fontWeight:700, color:'#fff'}}>{b.class}</span>
          </div>
        ))}
        {/* Patient marker */}
        <div style={{
          position:'absolute', top:-2, left:`${pct}%`, transform:'translateX(-50%)',
          width:20, height:36, display:'flex', flexDirection:'column', alignItems:'center'
        }}>
          <div style={{width:2, height:36, background:'#1A1A1A'}}/>
          <div style={{
            position:'absolute', top:-18, background:'#1A1A1A', color:'#fff',
            fontSize:9, fontWeight:700, padding:'2px 5px', borderRadius:2, whiteSpace:'nowrap'
          }}>{mayoClass}</div>
        </div>
      </div>
      <div style={{display:'flex', justifyContent:'space-between', marginTop:4}}>
        <span style={{fontSize:10, color:'#2E7D5B'}}>Slow progression</span>
        <span style={{fontSize:10, color:'#D32027'}}>Rapid progression</span>
      </div>
    </div>
  );
}

function DecisionAssistant({ patient, role }) {
  const [expanded, setExpanded] = useState(true);
  const [whyOpen, setWhyOpen] = useState(false);
  const [popover, setPopover] = useState(null);
  const [simEgfr, setSimEgfr] = useState(patient.egfr);

  const decisionColor = { 'Eligible':'#2E7D5B', 'Review':'#C77700', 'Not Eligible':'#D32027' };
  const dc = decisionColor[patient.decision];

  // Simulate: if simEgfr < 25, flip that criterion
  const simCriteria = patient.criteria.map(c => {
    if (c.loinc === '62238-1') {
      const met = simEgfr >= 25;
      return { ...c, met, value: `${simEgfr} ml/min/1.73m² (simulated)` };
    }
    return c;
  });
  const metCount = simCriteria.filter(c=>c.met).length;
  const simDecision = simEgfr < 25 ? 'Not Eligible' : patient.decision;
  const simConf = simEgfr < 25 ? 0.94 : patient.confidence;
  const simColor = decisionColor[simDecision];
  const simChanged = simEgfr !== patient.egfr;

  return (
    <div style={{borderTop:`3px solid ${simChanged ? '#C77700' : dc}`, overflow:'hidden'}}>
      {popover && <ProvenancePopover {...popover} patient={patient} onClose={()=>setPopover(null)}/>}
      {/* Header */}
      <div style={{background: simChanged ? '#FFFBEB' : simDecision==='Eligible'?'#F0FDF4':simDecision==='Review'?'#FFFBEB':'#FEF2F2', padding:'12px 16px', display:'flex', alignItems:'center', justifyContent:'space-between'}}>
        <div style={{display:'flex', alignItems:'center', gap:10}}>
          <div style={{
            width:10, height:10, borderRadius:'50%', background: simColor
          }}/>
          <div>
            <div style={{fontSize:10, fontWeight:600, color:'#4A4A4A', textTransform:'uppercase', letterSpacing:'0.06em'}}>
              Tolvaptan Decision Assistant {simChanged && '· SIMULATION'}
            </div>
            <div style={{fontSize:20, fontWeight:700, color: simColor, lineHeight:1.2, marginTop:2}}>
              {simDecision}
            </div>
          </div>
        </div>
        <div style={{textAlign:'right'}}>
          <div style={{fontSize:10, color:'#4A4A4A', marginBottom:3}}>Confidence</div>
          <div style={{fontSize:18, fontWeight:700, color: simColor}}>{Math.round(simConf*100)}%</div>
          <div style={{width:64, height:4, background:'#E5E5E7', borderRadius:2, marginTop:4, overflow:'hidden'}}>
            <div style={{width:`${simConf*100}%`, height:'100%', background:simColor, borderRadius:2}}/>
          </div>
        </div>
      </div>

      {/* Guidelines badge */}
      <div style={{padding:'6px 16px', background:'#F7F7F8', borderBottom:'1px solid #E5E5E7', fontSize:11, color:'#4A4A4A', display:'flex', gap:8}}>
        <span style={{fontWeight:600, color:'#1A1A1A'}}>Guideline basis:</span>
        <span>KDIGO 2025</span><span>·</span><span>ERA Genes & Kidney 2024</span>
        <span style={{marginLeft:'auto', color:'#2E7D5B', fontWeight:600}}>{metCount}/{simCriteria.length} criteria met</span>
      </div>

      {/* Why section toggle */}
      <div style={{padding:'8px 16px', borderBottom:'1px solid #E5E5E7'}}>
        <button onClick={() => setWhyOpen(o=>!o)} style={{
          background:'none', border:'none', cursor:'pointer', display:'flex', alignItems:'center', gap:6,
          fontSize:12, fontWeight:600, color:'#1A1A1A', padding:0
        }}>
          <span style={{
            display:'inline-block', transform: whyOpen?'rotate(90deg)':'rotate(0deg)',
            transition:'transform 0.2s', fontSize:10
          }}>▶</span>
          Why this recommendation?
        </button>
      </div>

      {/* Expandable criteria */}
      <div style={{
        maxHeight: whyOpen ? '600px' : '0', overflow:'hidden',
        transition:'max-height 0.3s ease', borderBottom: whyOpen ? '1px solid #E5E5E7':'none'
      }}>
        <div style={{padding:'8px 16px'}}>
          {simCriteria.map((c, i) => (
            <div key={i} style={{
              display:'flex', alignItems:'flex-start', gap:10, padding:'7px 0',
              borderBottom: i < simCriteria.length-1 ? '1px solid #F0F0F0' : 'none'
            }}>
              <span style={{fontSize:14, color: c.met ? '#2E7D5B' : '#D32027', marginTop:1, flexShrink:0}}>
                {c.met ? '✓' : '✗'}
              </span>
              <div style={{flex:1, minWidth:0}}>
                <div style={{fontSize:12, fontWeight:500, color:'#1A1A1A'}}>{c.text}</div>
                <div style={{fontSize:11, color:'#4A4A4A', marginTop:1}}>
                  <span style={{fontWeight:600, color: c.met?'#2E7D5B':'#D32027'}}>{c.value}</span>
                  <span style={{margin:'0 6px', color:'#D1D1D6'}}>·</span>
                  <span
                    onClick={()=>{
                      const t = (c.text||'').toLowerCase();
                      let field = c.text;
                      if (t.includes('egfr')) field = 'eGFR';
                      else if (t.includes('creatinine')) field = 'Creatinine';
                      else if (t.includes('cystatin')) field = 'Cystatin C';
                      else if (t.includes('urine') || t.includes('albumin')) field = 'Urine albumin/cr';
                      else if (t.includes('blood pressure') || t.includes('bp ')) field = 'Blood pressure (systolic)';
                      else if (t.includes('tkv')) field = 'htTKV';
                      setPopover({field, source:c.source, fhir:c.fhir, system:c.system, loinc:c.loinc, value:c.value});
                    }}
                    style={{color:'#D32027', cursor:'pointer', textDecoration:'underline', fontSize:11}}
                  >{c.source}</span>
                </div>
              </div>
            </div>
          ))}
        </div>
      </div>

      {/* What-if slider */}
      <div style={{padding:'12px 16px', background:'#FAFAFA'}}>
        <div style={{fontSize:11, fontWeight:600, color:'#4A4A4A', textTransform:'uppercase', letterSpacing:'0.06em', marginBottom:8}}>
          Simulation – what if eGFR changes?
        </div>
        <div style={{display:'flex', alignItems:'center', gap:10}}>
          <span style={{fontSize:11, color:'#4A4A4A', width:90}}>eGFR = <strong style={{color: simDecision==='Not Eligible'?'#D32027':'#1A1A1A'}}>{simEgfr} ml/min</strong></span>
          <input type="range" min={15} max={65} step={1} value={simEgfr}
            onChange={e=>setSimEgfr(Number(e.target.value))}
            style={{flex:1, accentColor:'#D32027', cursor:'pointer'}}
          />
          <button onClick={()=>setSimEgfr(patient.egfr)} style={{
            fontSize:11, padding:'3px 8px', border:'1px solid #E5E5E7', borderRadius:3,
            background:'#fff', cursor:'pointer', color:'#4A4A4A'
          }}>Reset</button>
        </div>
        {simChanged && (
          <div style={{marginTop:8, padding:'6px 10px', background:'#FFFBEB', border:'1px solid #C7770044', borderRadius:4, fontSize:11, color:'#C77700'}}>
            ⚠ Simulation active – eGFR adjusted to {simEgfr} ml/min. Decision: <strong>{simDecision}</strong>
          </div>
        )}
      </div>

      {/* Actions – only for Nephrologist */}
      {(role === 'Nephrologist' || role === 'Pharmacist') && (
        <div style={{padding:'10px 16px', display:'flex', gap:8, borderTop:'1px solid #E5E5E7'}}>
          <button style={{
            padding:'7px 14px', background:'#D32027', color:'#fff', border:'none',
            borderRadius:4, fontSize:12, fontWeight:600, cursor:'pointer'
          }}>Open Case Conference</button>
          <button style={{
            padding:'7px 14px', background:'#fff', color:'#1A1A1A', border:'1px solid #E5E5E7',
            borderRadius:4, fontSize:12, cursor:'pointer'
          }}>Request second opinion</button>
          <button style={{
            padding:'7px 14px', background:'#fff', color:'#1A1A1A', border:'1px solid #E5E5E7',
            borderRadius:4, fontSize:12, cursor:'pointer'
          }}>Export PDF</button>
        </div>
      )}
    </div>
  );
}

// ── Module renderers: each sub-module id → a JSX fragment ─────────────────
function renderModule(id, ctx) {
  const { patient, role, openProv, egfrData, tkvData } = ctx;
  switch (id) {
    case 'snap.vitals':
      return (
        <div style={{display:'grid', gridTemplateColumns:'1fr 1fr 1fr', gap:8}}>
          <DataPoint label="eGFR" value={patient.egfr} unit="ml/min/1.73m²"
            color={patient.egfr<30?'#D32027':patient.egfr<60?'#C77700':'#2E7D5B'}
            onClick={()=>openProv('egfr')}/>
          <DataPoint label="Creatinine" value={patient.creatinine} unit="mg/dl" onClick={()=>openProv('creatinine')}/>
          <DataPoint label="Cystatin C" value={patient.cystatinC} unit="mg/L" onClick={()=>openProv('cystatin')}/>
          <DataPoint label="Urine albumin/cr" value={patient.urineAlbumin} unit="mg/g"
            color={patient.urineAlbumin>30?'#C77700':'#2E7D5B'} onClick={()=>openProv('urine')}/>
          <DataPoint label="Blood pressure" value={patient.bp} unit="mmHg" onClick={()=>openProv('blood pressure')}/>
          <DataPoint label="CKD stage" value={patient.ckdStage} unit=""
            color={patient.ckdStage==='G4'?'#D32027':patient.ckdStage.startsWith('G3')?'#C77700':'#2E7D5B'}/>
        </div>
      );
    case 'snap.egfrchart':
      return (
        <div>
          <div style={{fontSize:11, color:'#4A4A4A', marginBottom:8, fontWeight:600, textTransform:'uppercase', letterSpacing:'0.05em'}}>eGFR trajectory (ml/min/1.73m²)</div>
          <div style={{height:130}}>
            <SvgLineChart data={egfrData} height={130} color="#D32027" fill="#D32027"
              refLines={[{v:60,color:'#C77700',label:'G3'},{v:30,color:'#D32027',label:'G4'},{v:25,color:'#D32027',label:'min'}]}/>
          </div>
        </div>
      );
    case 'snap.family':
      return <div style={{padding:'6px 10px', background:'#F7F7F8', borderRadius:4, fontSize:11, color:'#4A4A4A'}}><strong>Family history:</strong> {patient.familyHistory}</div>;
    case 'img.metrics':
      return (
        <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8}}>
          <DataPoint label="htTKV" value={patient.htTKV} unit="ml/m" color="#C77700" onClick={()=>openProv('tkv')}/>
          <DataPoint label="Annual TKV growth" value="+6.2" unit="%/yr" color="#D32027" onClick={()=>openProv('tkv')}/>
        </div>
      );
    case 'img.mayoband':
      return <MayoBandChart mayoClass={patient.mayoClass} htTKV={patient.htTKV}/>;
    case 'img.tkvchart':
      return (
        <div>
          <div style={{fontSize:11, color:'#4A4A4A', marginBottom:6, fontWeight:600, textTransform:'uppercase', letterSpacing:'0.05em'}}>htTKV trajectory (ml/m)</div>
          <div style={{height:100}}><SvgLineChart data={tkvData} height={100} color="#C77700" fill="#C77700"/></div>
        </div>
      );
    case 'img.pacs':
      return (
        <div style={{border:'1px solid #E5E5E7', borderRadius:4, background:'#F7F7F8', padding:14, display:'flex', alignItems:'center', gap:12}}>
          <div style={{width:56, height:56, borderRadius:4, background:'repeating-linear-gradient(45deg,#E5E5E7,#E5E5E7 2px,#F7F7F8 2px,#F7F7F8 8px)', display:'flex', alignItems:'center', justifyContent:'center', flexShrink:0}}>
            <span style={{fontSize:8, color:'#4A4A4A', fontFamily:'monospace', textAlign:'center'}}>MRI<br/>PACS</span>
          </div>
          <div>
            <div style={{fontSize:12, fontWeight:600, color:'#1A1A1A'}}>AI-segmented kidney volume</div>
            <div style={{fontSize:11, color:'#4A4A4A', marginTop:2}}>Syngo.via · 2026-03-21 · Bilateral T2 MRI</div>
            <div style={{fontSize:11, color:'#D32027', marginTop:3, fontWeight:500}}>→ Open in PACS viewer</div>
          </div>
        </div>
      );
    case 'gen.variant':
      return (
        <div style={{padding:'10px 12px', background:'#F7F7F8', border:'1px solid #E5E5E7', borderRadius:4}}>
          <div style={{fontSize:11, color:'#4A4A4A', marginBottom:4}}>Identified variant</div>
          <div style={{fontSize:14, fontWeight:700, color:'#1A1A1A'}}>{patient.pkdVariant}</div>
          <div style={{fontSize:12, fontFamily:'monospace', color:'#4A4A4A', marginTop:4}}>{patient.pkdDetail}</div>
        </div>
      );
    case 'gen.acmg':
      return (
        <div>
          <div style={{fontSize:11, color:'#4A4A4A', marginBottom:6, fontWeight:600, textTransform:'uppercase', letterSpacing:'0.05em'}}>ACMG Pathogenicity class</div>
          <div style={{display:'flex', gap:4}}>
            {['Benign','Likely benign','VUS','Likely pathogenic','Pathogenic'].map((cls, i) => {
              const isActive = patient.pkdDetail.includes('class 5') && i === 4
                || patient.pkdDetail.includes('class 4') && i === 3
                || patient.pkdDetail.includes('class 3') && i === 2;
              return (
                <div key={cls} style={{flex:1, padding:'6px 4px', textAlign:'center', borderRadius:3,
                  background: isActive ? (i>=3?'#2E7D5B':'#C77700') : '#E5E5E7',
                  color: isActive ? '#fff' : '#4A4A4A', fontSize:9, fontWeight: isActive?700:400, lineHeight:1.3}}>{cls}</div>
              );
            })}
          </div>
        </div>
      );
    case 'gen.compare':
      return (
        <div>
          <div style={{fontSize:11, color:'#4A4A4A', marginBottom:6, fontWeight:600, textTransform:'uppercase', letterSpacing:'0.05em'}}>Gene · prognostic context</div>
          <div style={{display:'grid', gridTemplateColumns:'1fr 1fr', gap:8}}>
            {[
              { gene:'PKD1', sub:'Truncating', egfrLoss:'–4–6 ml/min/yr', esrdAge:'~55', active: patient.pkdVariant.includes('PKD1 trunc') },
              { gene:'PKD1', sub:'Non-truncating', egfrLoss:'–2–4 ml/min/yr', esrdAge:'~67', active: patient.pkdVariant.includes('PKD1 non') },
              { gene:'PKD2', sub:'Any class', egfrLoss:'–1–2 ml/min/yr', esrdAge:'~79', active: patient.pkdVariant.includes('PKD2') },
              { gene:'Other / unknown', sub:'–', egfrLoss:'Variable', esrdAge:'Variable', active: false },
            ].map((row, i) => (
              <div key={i} style={{padding:'8px 10px', borderRadius:4, fontSize:11,
                border: row.active ? '2px solid #1A1A1A' : '1px solid #E5E5E7',
                background: row.active ? '#1A1A1A' : '#F7F7F8',
                color: row.active ? '#fff' : '#4A4A4A'}}>
                <div style={{fontWeight:700, fontSize:12, color: row.active?'#fff':'#1A1A1A'}}>{row.gene}</div>
                <div style={{marginTop:2, opacity:0.8}}>{row.sub}</div>
                <div style={{marginTop:4, fontSize:10}}>eGFR loss: {row.egfrLoss}</div>
                <div style={{fontSize:10}}>Median ESRD: ~{row.esrdAge} yr</div>
              </div>
            ))}
          </div>
        </div>
      );
    case 'gen.consent':
      return <div style={{padding:'6px 10px', background:'#F0FDF4', border:'1px solid #2E7D5B44', borderRadius:4, fontSize:11, color:'#2E7D5B'}}>✓ Genetic counselling documented (2025-11-03) · DIZ consent: specific</div>;
    case 'dec.full':
      return <DecisionAssistant patient={patient} role={role}/>;
  }
  return null;
}

// Module title lookup
function moduleTitle(id) {
  return MODULE_CATALOGUE.find(m=>m.id===id)?.title || id;
}
function moduleParentOf(id) {
  return MODULE_CATALOGUE.find(m=>m.id===id)?.parent || '';
}

// Module → parent panel mapping for grid grouping
function moduleParent(id) {
  const m = MODULE_CATALOGUE.find(m=>m.id===id);
  return m ? m.parent : 'Other';
}
function panelSubtitle(parent, patient, role) {
  switch (parent) {
    case 'Clinical Snapshot': return `CKD Stage ${patient.ckdStage} · BP ${patient.bp} mmHg`;
    case 'Imaging & Volumetry': return `htTKV ${patient.htTKV} ml/m · Mayo ${patient.mayoClass}`;
    case 'Genetics': return 'PKD variant classification';
    case 'Decision Assistant': return `KDIGO 2025 · ERA Genes & Kidney 2024 · Role: ${role}`;
  }
  return '';
}

function PatientDashboard({ patient, role, onOpenProvenance }) {
  const [popover, setPopover] = useState(null);
  const [dismissedMods, setDismissedMods] = useState(new Set());
  const [views, setViews] = useViews();
  const [activeViewId, setActiveViewId] = useState('seed-full');
  const [configOpen, setConfigOpen] = useState(false);
  const [editingId, setEditingId] = useState(null);

  // Auto-select an available view when patient changes
  useEffect(() => {
    const avail = availableViewsFor(views, patient.id);
    if (!avail.some(v=>v.id===activeViewId)) setActiveViewId(avail[0]?.id || 'seed-full');
  }, [patient.id]);

  // Reset dismissed tiles when view or patient changes
  useEffect(() => { setDismissedMods(new Set()); }, [activeViewId, patient.id]);

  const activeView = views.find(v=>v.id===activeViewId) || views[0];
  const enabledMods = new Set(normalizeModules(activeView?.modules || []).map(m => m.id));

  const provenanceLookup = (field) => {
    const p = patient.provenance.find(p => p.field.toLowerCase().includes(field.toLowerCase()));
    return p || { field, system:'KIS/LIS', date:'2026-04-14', loinc:'N/A', consent:'Broad MII', status:'Validated', standardization:'–' };
  };

  // Short alias → canonical field name recognised by FIELD_CONFIG
  const FIELD_ALIASES = {
    'egfr':'eGFR',
    'creatinine':'Creatinine',
    'cystatin':'Cystatin C',
    'urine':'Urine albumin/cr',
    'blood pressure':'Blood pressure (systolic)',
    'tkv':'htTKV',
  };

  const openProv = (field) => {
    const canonical = FIELD_ALIASES[field] || field;
    const c = patient.criteria.find(c => c.loinc && c.loinc !== 'N/A' && c.text.toLowerCase().includes(field.toLowerCase()));
    if (c) {
      setPopover({ field: canonical, source: c.source, fhir: c.fhir, system: c.system, loinc: c.loinc, value: c.value });
    } else {
      const p = provenanceLookup(field);
      setPopover({ field: canonical, source: p.system, fhir:`Observation/${canonical.toLowerCase().replace(/\s/g,'-')}-001`, system:p.system.split(' ')[0], loinc:p.loinc, value:'See history chart' });
    }
  };

  const egfrData = patient.egfrTrend;
  const tkvData = patient.tkvTrend;

  return (
    <div style={{height:'100%', display:'flex', flexDirection:'column', overflow:'hidden'}}>
      {popover && <ProvenancePopover {...popover} patient={patient} onClose={()=>setPopover(null)}/>}

      {/* Patient header strip */}
      <div style={{padding:'12px 24px', borderBottom:'1px solid #E5E5E7', background:'#fff', display:'flex', alignItems:'center', gap:24, flexShrink:0}}>
        <div style={{width:36, height:36, borderRadius:'50%', background:'#E5E5E7', display:'flex', alignItems:'center', justifyContent:'center', fontWeight:700, color:'#4A4A4A', fontSize:14, flexShrink:0}}>
          {patient.alias.split(',')[0][0]}
        </div>
        <div>
          <div style={{display:'flex', alignItems:'center', gap:10}}>
            <span style={{fontSize:16, fontWeight:700, color:'#1A1A1A'}}>{patient.alias}</span>
            <span style={{fontSize:12, color:'#4A4A4A', fontFamily:'monospace'}}>{patient.id}</span>
            <span style={{
              fontSize:11, fontWeight:600, padding:'2px 7px', borderRadius:3,
              background: patient.riskTier==='Rapid'?'#FEF2F2':patient.riskTier==='Intermediate'?'#FFFBEB':'#F0FDF4',
              color: patient.riskTier==='Rapid'?'#D32027':patient.riskTier==='Intermediate'?'#C77700':'#2E7D5B'
            }}>{patient.riskTier} progressor</span>
          </div>
          <div style={{fontSize:12, color:'#4A4A4A', marginTop:2}}>
            {patient.sex === 'F' ? 'Female' : 'Male'}, {patient.age} yr · CKD {patient.ckdStage} · ADPKD · Last update {patient.lastUpdate}
          </div>
        </div>
        <div style={{marginLeft:'auto', display:'flex', alignItems:'center', gap:8}}>
          <ViewSelector
            views={views}
            activeViewId={activeViewId}
            setActiveViewId={setActiveViewId}
            patient={patient}
            onConfigure={()=>{ setEditingId(null); setConfigOpen(true); }}
            onEdit={(id)=>{ setEditingId(id); setConfigOpen(true); }}
          />
          <button onClick={onOpenProvenance} style={{
            padding:'6px 12px', background:'#F7F7F8', border:'1px solid #E5E5E7',
            borderRadius:4, fontSize:12, cursor:'pointer', color:'#1A1A1A'
          }}>Data provenance</button>
        </div>
      </div>

      <ConfigureModal
        open={configOpen}
        onClose={()=>{ setConfigOpen(false); setEditingId(null); }}
        views={views}
        setViews={setViews}
        activeViewId={activeViewId}
        setActiveViewId={setActiveViewId}
        patient={patient}
        editingId={editingId}
      />

      {/* View-driven panel grid: groups modules by parent panel */}
      <div style={{flex:1, overflow:'auto', padding:16}}>
        {(() => {
          const parents = ['Clinical Snapshot','Imaging & Volumetry','Genetics','Decision Assistant'];
          const activePanels = parents.filter(p =>
            MODULE_CATALOGUE.some(m => m.parent===p && enabledMods.has(m.id) && !dismissedMods.has(m.id))
          );
          if (activePanels.length === 0) {
            return (
              <div style={{padding:'60px 20px', textAlign:'center', color:'#AEAEB2', fontSize:13}}>
                <div style={{fontSize:14, fontWeight:600, color:'#4A4A4A', marginBottom:6}}>This view is empty</div>
                <div>Use <strong>+ New view</strong> or pick another view above.</div>
              </div>
            );
          }
          const cols = activePanels.length === 1 ? '1fr' : '1fr 1fr';
          const ctx = { patient, role, openProv, egfrData, tkvData };
          return (
            <div style={{display:'grid', gridTemplateColumns:cols, gap:12, minWidth: activePanels.length>1?900:0, alignItems:'start'}}>
              {activePanels.map(parent => {
                const mods = MODULE_CATALOGUE.filter(m => m.parent===parent && enabledMods.has(m.id));
                const isDecision = parent === 'Decision Assistant';
                return (
                  <Panel key={parent}
                         title={parent==='Decision Assistant'?'Tolvaptan Decision Assistant':parent}
                         subtitle={panelSubtitle(parent, patient, role)}>
                    {isDecision ? (
                      <DecisionAssistant patient={patient} role={role}/>
                    ) : (
                      <div style={{display:'flex', flexDirection:'column', gap:12}}>
                        {mods.filter(m => !dismissedMods.has(m.id)).map(m => (
                          <div key={m.id} style={{position:'relative'}}>
                            <button
                              onClick={()=>{
                                const next = new Set(dismissedMods);
                                next.add(m.id);
                                setDismissedMods(next);
                              }}
                              title="Dismiss (restore via Configure view)"
                              style={{
                                position:'absolute', top:-4, right:-4, zIndex:3,
                                width:18, height:18, borderRadius:'50%',
                                background:'#fff', border:'1px solid #E5E5E7',
                                cursor:'pointer', fontSize:12, lineHeight:'14px',
                                color:'#4A4A4A', padding:0,
                                display:'flex', alignItems:'center', justifyContent:'center',
                                boxShadow:'0 1px 2px rgba(0,0,0,0.04)'
                              }}
                            >×</button>
                            {renderModule(m.id, ctx)}
                          </div>
                        ))}
                      </div>
                    )}
                  </Panel>
                );
              })}
            </div>
          );
        })()}
      </div>

    </div>
  );
}

Object.assign(window, { PatientDashboard });
