/* ============================================================
   calibrate.js — Calibration 탭 (COPASI joint 보정 + 정직한 검증)
   ============================================================ */
(function () {
  'use strict';
  let charts = {}, inited = false;
  const AXIS_COLOR = { W: '#2f63c8', B: '#7c3aed', S: '#0e7490', D: '#1f8a5b', H: '#c2367f', F: '#c0561f', A: '#b07a12' };
  function el(id) { return document.getElementById(id); }
  function num(v, d) { return (v === null || v === undefined || isNaN(v)) ? '—' : (+v).toFixed(d); }

  function init() {
    if (inited) return; inited = true;
    const cal = Store.calibration;
    const sel = el('cal-target');
    if (!cal || !cal.targets) {
      el('cal-summary').innerHTML = '<div class="empty-state">calibration_result.json 없음 — <code>python -m digital_twin.copasi_calibrate</code> 실행 필요.</div>';
      return;
    }
    Object.keys(cal.targets).forEach(k => sel.add(new Option((cal.targets[k] || {}).source_label || k, k)));
    sel.addEventListener('change', () => render(sel.value));
    renderValidation();
    renderHeader();
    renderCoupled();
    render(sel.value || Object.keys(cal.targets)[0]);
  }

  function renderCoupled() {
    const box = el('cal-coupled'); if (!box) return;
    const cp = Store.coupled;
    if (!cp || !cp.scenarios) { box.innerHTML = '<span class="panel-subtitle">coupled_scenarios.json 없음</span>'; return; }
    const lab = s => (s.interventions || []).length ? s.disease + ' + ' + s.interventions.join('+') : s.disease;
    box.innerHTML = cp.scenarios.map(s => {
      const af = s.anagen_fraction_pct_of_healthy, ph = s.peak_hair_pct_of_healthy;
      const cls = af >= 70 ? 'good' : af >= 30 ? 'warn' : 'bad';
      return `<div class="coupled-row">
        <span class="coupled-lab">${lab(s)}</span>
        <span class="coupled-bar-wrap"><span class="coupled-bar ${cls}" style="width:${Math.min(100, af)}%"></span></span>
        <span class="coupled-val">anagen ${num(af, 0)}% · peak ${num(ph, 0)}%</span>
      </div>`;
    }).join('');
  }

  function renderValidation() {
    const box = el('cal-validation'); if (!box) return;
    const v = Store.validation;
    if (!v || !v.summary) { box.innerHTML = ''; return; }
    const s = v.summary;
    box.innerHTML = `
      <div class="val-head">🔬 독립 검증 — 트윈 예측 vs 실제 논문 실험결과 (out-of-sample)</div>
      <div class="val-cards">
        <div class="val-card big"><div class="vv">${(s.twin_causal_accuracy*100).toFixed(1)}%</div><div class="vl">논문 인과 실험 일치 (n=${s.n_causal})</div></div>
        <div class="val-card"><div class="vv">p${s.permutation_p < 0.0001 ? '<0.0001' : '='+s.permutation_p}</div><div class="vl">permutation (우연 대비)</div></div>
        <div class="val-card"><div class="vv">[${(s.bootstrap_ci95||[]).map(x=>(x*100).toFixed(0)).join(', ')}]%</div><div class="vl">bootstrap 95% CI (1만회)</div></div>
        <div class="val-card"><div class="vv">${s.ground_truth_records}</div><div class="vl">논문 실험결과 GT (멀티에이전트)</div></div>
        <div class="val-card"><div class="vv">${(s.external_transcriptomic_accuracy*100).toFixed(0)}%</div><div class="vl">외부 GSE36169 (조성교란·참고)</div></div>
      </div>
      <div class="val-note">✅ 수정: ${(s.fixes_applied||[]).join(' · ')} &nbsp;|&nbsp; 트윈이 옳고 독립소스가 틀린 예: ${(s.twin_correct_where_sources_err||[]).join('; ')}</div>
      ${v.protein_modules ? `<div class="val-pm">🧬 단백질 분석 모듈 교차검증 (알파폴드 외): STRING PPI 축 응집 <b>${v.protein_modules.axes_ppi_significant}</b> (p&lt;0.05) · 축 라벨일치 <b>${v.protein_modules.axes_label_matched}</b> · 질환 생물학일치 <b>${v.protein_modules.diseases_biology_matched}</b> · 실험 PDB <b>${v.protein_modules.experimental_pdb}</b></div>` : ''}
      <div class="val-note dim">⚠️ ${(v.honest_caveats||[]).join(' · ')}</div>`;
  }

  function renderHeader() {
    const cal = Store.calibration, s = cal.summary || {}, uq = cal.uncertainty || {};
    const jr = s.joint_r2 || {};
    const robust = (s.robust_axes || []).map(a => (cal.targets.reference.axis_label || {})[a] || a).join(', ');
    el('cal-summary').innerHTML = `
      <div class="cal-card big"><div class="mv">${num(s.loocv_cv_r2, 2)}</div><div class="ml">LOOCV 교차검증 R² (외표본·정직 지표)</div></div>
      <div class="cal-card"><div class="mv">${num(jr.reference, 2)}</div><div class="ml">joint R² · 문헌(형상)</div></div>
      <div class="cal-card"><div class="mv">${num(jr.gse11186, 2)}</div><div class="ml">joint R² · 실측 GSE11186</div></div>
      <div class="cal-card"><div class="mv">${cal.n_fit_params}<span style="font-size:.5em">+${cal.n_fixed_params}fix</span></div><div class="ml">추정/고정 파라미터</div></div>
      <div class="cal-card"><div class="mv">${num(cal.aicc, 0)}</div><div class="ml">AICc (복잡도 penalized)</div></div>
      <div class="cal-card wide"><div class="mv-sm">견고 축: ${robust || '—'} · 비식별 ${uq.n_poorly_identified}/${cal.n_fit_params} · 경계고착 ${uq.n_at_bound}</div><div class="ml">${cal.approach || ''}</div></div>`;
    // 정직성 배너
    let banner = el('cal-banner');
    if (!banner) { banner = document.createElement('div'); banner.id = 'cal-banner'; banner.className = 'cal-banner';
      el('cal-summary').insertAdjacentElement('afterend', banner); }
    banner.innerHTML = `⚖️ <b>정직한 보정</b>: 단일적합 R²(문헌 ${num((cal.descriptive_single_r2||{}).reference,2)}, 실측 ${num((cal.descriptive_single_r2||{}).gse11186,2)})은 데이터셋 간 전이 안 되는 <i>서술적 상한</i>. 1차 지표는 <b>단일 파라미터셋으로 두 데이터셋을 동시 적합한 joint R²</b>와 <b>LOOCV</b>. ${s.honesty_note || ''}`;
  }

  function render(key) {
    const cal = Store.calibration;
    const tg = (cal.targets || {})[key];
    if (!tg) return;
    const grid = el('cal-grid');
    Object.values(charts).forEach(c => c.destroy()); charts = {};
    const obs = tg.observed || [];
    grid.innerHTML = obs.filter(st => tg.model_dense && tg.model_dense[st] && tg.data && tg.data[st]).map(st => {
      const fq = (tg.fit_quality || {})[st] || {};
      const r2 = fq.r2;
      const cls = r2 >= 0.7 ? 'good' : r2 >= 0.3 ? 'warn' : 'bad';
      return `<div class="cal-axis-card">
         <div class="cal-axis-head"><b>${(tg.axis_label || {})[st] || st}</b>
           <span class="cal-r2 ${cls}">R²=${num(r2, 2)}</span></div>
         <div class="cal-axis-chart"><canvas id="cal-c-${st}"></canvas></div>
       </div>`;
    }).join('');

    obs.forEach(st => {
      if (!el(`cal-c-${st}`)) return;
      const col = AXIS_COLOR[st] || '#6699ff';
      const modelPts = (tg.model_dense_t || []).map((t, i) => ({ x: t, y: tg.model_dense[st][i] }));
      const dataPts = (tg.timepoints_days || []).map((t, i) => ({ x: t, y: tg.data[st][i] }));
      charts[st] = new Chart(el(`cal-c-${st}`), {
        data: { datasets: [
          { type: 'line', label: '모델(joint 보정)', data: modelPts, borderColor: col, borderWidth: 2, pointRadius: 0, tension: .3 },
          { type: 'scatter', label: '데이터', data: dataPts, backgroundColor: '#1b1a16', borderColor: '#1b1a16', pointRadius: 3.5 },
        ] },
        options: { responsive: true, maintainAspectRatio: false,
          scales: { x: { type: 'linear', min: 0, ticks: { color: '#6b655a', font: { size: 10 }, callback: v => v + 'd' }, grid: { color: 'rgba(0,0,0,.08)' } },
                    y: { min: -0.05, max: 1.1, ticks: { color: '#6b655a', font: { size: 10 } }, grid: { color: 'rgba(0,0,0,.08)' } } },
          plugins: { legend: { display: false } } },
      });
    });

    // 추정 파라미터 + 식별가능성
    const fp = cal.fitted_params || {}, uq = cal.uncertainty || {}, rse = uq.rel_stderr || {};
    const fitNames = cal.fit_param_names || [];
    el('cal-nparam').textContent = fitNames.length;
    el('cal-param-table').innerHTML = fitNames.map(k => {
      const poor = (uq.poorly_identified || []).includes(k);
      const bound = (uq.params_at_bound || []).includes(k);
      const flag = bound ? '⚠경계' : poor ? '·비식별' : '';
      return `<div class="cal-prow"><span class="pk">${k}${flag ? ' <span class="pflag">' + flag + '</span>' : ''}</span><span class="pi">${num(rse[k], 2)}</span><span class="pa">${num(fp[k], 3)}</span></div>`;
    }).join('');

    // provenance + UQ 요약
    const corr = (uq.strong_correlations || []).slice(0, 6).map(c => `${c[0]}~${c[1]}(${c[2]})`).join(', ');
    el('cal-provenance').innerHTML =
      `<p class="cal-note">${tg.is_real_data ? '✅ 실측 데이터' : '📐 문헌 합성(형상복원 점검)'} — ${tg.notes || ''}</p>` +
      `<p style="color:var(--warn);font-size:.72rem">불확실성: 비식별 ${uq.n_poorly_identified}/${fitNames.length} · 경계고착 ${uq.n_at_bound} · 강상관쌍: ${corr || '없음'} · LOOCV CV-R²=${num((cal.loocv_reference||{}).cv_r2,2)}</p>` +
      '<ul>' + (tg.provenance || []).map(p => `<li>${p}</li>`).join('') + '</ul>';
  }

  window.CalibrateTab = { init };
})();
