// ════════════════════════════════════════════════════════════════
//  WEEKLY S-CURVE — ทฤษฎีงานก่อสร้างที่ถูกต้อง
//  หลักการ (civil-lead):
//   • S-Curve = % ความก้าวหน้า "สะสม" (Y) เทียบกับ "เวลา-รายสัปดาห์" (X)
//   • แต่ละกิจกรรมมี "น้ำหนักงาน (weight %)" = สัดส่วนมูลค่า/แรงงาน (รวม = 100%)
//   • % สะสมตามแผน(สัปดาห์ w) = Σ [weight × สัดส่วนเวลาที่ผ่านไปของกิจกรรม]
//     → กิจกรรมที่ทับซ้อนกันทำให้เกิดรูป "S" (เริ่มช้า-เร่งกลาง-ชะลอท้าย)
//   • มี 2 baseline:  Master Plan (แผนรวมหยาบ)  +  Action Plan (แผนปฏิบัติละเอียด)
//   • % สะสมจริง = Σ [weight × % แล้วเสร็จจริงของกิจกรรม]  (ถ่วงน้ำหนัก)
//   • บันทึกผลเป็น "snapshot รายสัปดาห์" (วันที่ + % สะสม) → ลากเป็นเส้นจริง
// ════════════════════════════════════════════════════════════════

window.SCurveManager = window.SCurveManager || (function () {
  const KEY = 'syk-scurve-v1';
  const load = () => { try { return JSON.parse(localStorage.getItem(KEY) || '{}'); } catch { return {}; } };
  const save = (o) => localStorage.setItem(KEY, JSON.stringify(o));
  return {
    get(code) { return load()[code] || null; },
    set(code, data) { const all = load(); all[code] = { ...(all[code] || {}), ...data }; save(all); },
    addSnapshot(code, snap) {
      const all = load(); if (!all[code]) all[code] = {};
      const list = (all[code].snapshots || []).filter(s => s.date !== snap.date);
      list.push(snap); list.sort((a, b) => a.date.localeCompare(b.date));
      all[code].snapshots = list; save(all);
    },
    removeSnapshot(code, date) {
      const all = load(); if (!all[code]) return;
      all[code].snapshots = (all[code].snapshots || []).filter(s => s.date !== date); save(all);
    },
  };
})();

// สัดส่วนเวลาที่ผ่านไปของกิจกรรม ณ สัปดาห์ w (ramp เชิงเส้น)
function _actFrac(w, start, end) {
  start = Number(start) || 1; end = Number(end) || start;
  if (end < start) end = start;
  if (w < start) return 0;
  if (w >= end) return 1;
  return (w - start + 1) / (end - start + 1);
}

// น้ำหนักประสิทธิผล (effective weight) — ถ้ามี Amount ใช้สัดส่วน Amount, ไม่งั้นใช้ weight ที่กรอก
function effWeights(activities) {
  const totalAmt = activities.reduce((s, a) => s + (Number(a.amount) || 0), 0);
  if (totalAmt > 0) return activities.map(a => (Number(a.amount) || 0) / totalAmt * 100);
  const totalW = activities.reduce((s, a) => s + (Number(a.weight) || 0), 0) || 100;
  return activities.map(a => (Number(a.weight) || 0) / totalW * 100);
}

// % สะสมตามแผนทุกสัปดาห์ 1..N สำหรับ baseline 'm' (Plan baseline) หรือ 'a' (Action)
function buildPlanCurve(activities, weeks, base) {
  const W = effWeights(activities); // รวม = 100
  const out = [];
  for (let w = 1; w <= weeks; w++) {
    let cum = 0;
    activities.forEach((a, i) => {
      const s = base === 'm' ? a.m_start : (a.a_start || a.m_start);
      const e = base === 'm' ? a.m_end : (a.a_end || a.m_end);
      cum += W[i] * _actFrac(w, s, e);
    });
    out.push(+cum.toFixed(1));
  }
  return out;
}

function _isoWeekIndex(startISO, dateISO) {
  const s = new Date(startISO + 'T00:00:00'); const d = new Date(dateISO + 'T00:00:00');
  return Math.max(1, Math.round((d - s) / (7 * 86400000)) + 1);
}
function _fmtShort(n) {
  if (n == null) return '—';
  if (Math.abs(n) >= 1e6) return (n / 1e6).toFixed(2) + 'M';
  if (Math.abs(n) >= 1e3) return (n / 1e3).toFixed(0) + 'K';
  return Math.round(n).toLocaleString();
}

// ─── Main panel ────────────────────────────────────────────────
function WeeklySCurvePanel({ project, contract, lang, toast }) {
  const code = project.code;
  const [data, setData] = React.useState(() => window.SCurveManager.get(code));
  const [tab, setTab] = React.useState('chart'); // chart | plan | actual
  const [v, setV] = React.useState(0);

  React.useEffect(() => { setData(window.SCurveManager.get(code)); }, [code, v]);

  // Initialise from contract if empty
  const initFromContract = () => {
    const start = contract?.signed || new Date().toISOString().split('T')[0];
    let weeks = 16;
    if (contract?.signed && contract?.end) {
      weeks = Math.max(4, _isoWeekIndex(contract.signed, contract.end));
    }
    // Activities from contract work groups (weight = value share) else milestones
    let acts = [];
    const groups = contract?.work_groups || [];
    const totalVal = contract?.value || groups.reduce((s, g) => s + (g.total || 0), 0);
    if (groups.length > 0 && totalVal > 0) {
      const per = Math.max(1, Math.floor(weeks / groups.length));
      acts = groups.map((g, i) => ({
        id: 'a' + i, name: g.group_th || g.name || `หมวด ${g.no}`,
        amount: g.total || 0,
        weight: +(((g.total || 0) / totalVal) * 100).toFixed(1),
        m_start: i * per + 1, m_end: Math.min(weeks, (i + 1) * per),
        a_start: i * per + 1, a_end: Math.min(weeks, (i + 1) * per),
        actual_pct: 0,
      }));
    } else if ((contract?.milestones || []).length > 0) {
      const ms = contract.milestones;
      const per = Math.max(1, Math.floor(weeks / ms.length));
      acts = ms.map((m, i) => ({
        id: 'a' + i, name: m.name || `งวด ${m.no}`,
        amount: Number(m.amount) || 0,
        weight: +(Number(m.pct) || (100 / ms.length)).toFixed(1),
        m_start: i * per + 1, m_end: Math.min(weeks, (i + 1) * per),
        a_start: i * per + 1, a_end: Math.min(weeks, (i + 1) * per),
        actual_pct: 0,
      }));
    } else {
      acts = [{ id: 'a0', name: lang === 'th' ? 'งานทั้งหมด' : 'All work', weight: 100, m_start: 1, m_end: weeks, a_start: 1, a_end: weeks, actual_pct: 0 }];
    }
    window.SCurveManager.set(code, { start_date: start, weeks, activities: acts, snapshots: [] });
    setV(x => x + 1);
    toast(lang === 'th' ? '✓ สร้างโครงร่าง S-Curve จากสัญญาแล้ว — ปรับน้ำหนัก/สัปดาห์ได้' : 'Initialised from contract', 'success');
  };

  if (!data) {
    return (
      <div className="card" style={{ marginBottom: 14 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
          <span style={{ fontSize: 16 }}>📈</span>
          <h3 className="h2" style={{ margin: 0 }}>{lang === 'th' ? 'S-Curve รายสัปดาห์ (ถ่วงน้ำหนักงาน)' : 'Weekly S-Curve'}</h3>
        </div>
        <p style={{ fontSize: 12.5, color: 'var(--ink-soft)', lineHeight: 1.7, marginBottom: 12 }}>
          {lang === 'th'
            ? 'S-Curve ที่ถูกหลัก = % ความก้าวหน้าสะสมรายสัปดาห์ ถ่วงน้ำหนักแต่ละงาน เทียบ 2 แผน (Master/Action) กับผลจริง เริ่มต้นโดยดึงหมวดงาน+ระยะเวลาจากสัญญา'
            : 'Set up a weight-loaded weekly S-Curve from the contract.'}
        </p>
        <button className="btn btn-primary" onClick={initFromContract} disabled={!contract}>
          {contract ? (lang === 'th' ? '✨ สร้างจากสัญญา' : 'Init from contract') : (lang === 'th' ? 'ต้องมีสัญญาก่อน' : 'Need contract')}
        </button>
      </div>
    );
  }

  const { start_date, weeks, activities = [], snapshots = [] } = data;
  const masterCurve = buildPlanCurve(activities, weeks, 'm');
  const actionCurve = buildPlanCurve(activities, weeks, 'a');
  const weightSum = activities.reduce((s, a) => s + (Number(a.weight) || 0), 0);

  // actual points by week
  const actualPts = snapshots.map(s => ({ week: _isoWeekIndex(start_date, s.date), pct: s.cum_pct, date: s.date }));
  const currentWeek = _isoWeekIndex(start_date, new Date().toISOString().split('T')[0]);
  const planNow = actionCurve[Math.min(weeks, currentWeek) - 1] || 0;
  const actualNow = actualPts.length ? actualPts[actualPts.length - 1].pct : 0;
  const spi = planNow > 0 ? actualNow / planNow : 0;
  const variance = actualNow - planNow;

  const persist = (patch) => { window.SCurveManager.set(code, patch); setV(x => x + 1); };
  const updateAct = (id, key, val) => persist({ activities: activities.map(a => a.id === id ? { ...a, [key]: val } : a) });
  const addAct = () => persist({ activities: [...activities, { id: 'a' + Date.now().toString(36), name: '', weight: 0, m_start: 1, m_end: weeks, a_start: 1, a_end: weeks, actual_pct: 0 }] });
  const removeAct = (id) => persist({ activities: activities.filter(a => a.id !== id) });

  // weighted actual % from activities (อิงน้ำหนักจาก Amount)
  const W = effWeights(activities);
  const weightedActual = +(activities.reduce((s, a, i) => s + W[i] * (Number(a.actual_pct) || 0) / 100, 0)).toFixed(1);
  const totalAmount = activities.reduce((s, a) => s + (Number(a.amount) || 0), 0);

  return (
    <div className="card" style={{ marginBottom: 14 }}>
      <div style={{ display: 'flex', alignItems: 'center', justifyContent: 'space-between', marginBottom: 10, flexWrap: 'wrap', gap: 8 }}>
        <div style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
          <span style={{ fontSize: 16 }}>📈</span>
          <h3 className="h2" style={{ margin: 0 }}>{lang === 'th' ? 'S-Curve รายสัปดาห์ (ถ่วงน้ำหนักงาน) — Plan / Action / Actual' : 'Weekly S-Curve'}</h3>
        </div>
        <div style={{ display: 'flex', gap: 10, alignItems: 'center', flexWrap: 'wrap' }}>
          <div style={{ display: 'flex', gap: 8, alignItems: 'flex-end' }}>
            <div>
              <div className="micro" style={{ marginBottom: 2 }}>{lang === 'th' ? 'เริ่ม' : 'Start'}</div>
              <input type="date" value={start_date} onChange={(e) => persist({ start_date: e.target.value })} style={{ ...scInp, width: 130 }} />
            </div>
            <div>
              <div className="micro" style={{ marginBottom: 2 }}>{lang === 'th' ? 'สัปดาห์' : 'Weeks'}</div>
              <input type="number" min="1" max="200" value={weeks} onChange={(e) => persist({ weeks: Math.max(1, Number(e.target.value)) })} style={{ ...scInp, width: 64 }} />
            </div>
          </div>
        </div>
      </div>

      {/* KPIs */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(4, 1fr)', gap: 8, marginBottom: 12 }}>
        <SCKpi label={lang === 'th' ? 'สัปดาห์ปัจจุบัน' : 'Week'} value={`W${Math.min(weeks, currentWeek)}/${weeks}`} />
        <SCKpi label={lang === 'th' ? '% ตามแผน (Action)' : 'Plan %'} value={planNow.toFixed(1) + '%'} color="#6f86ff" />
        <SCKpi label={lang === 'th' ? '% จริง' : 'Actual %'} value={actualNow.toFixed(1) + '%'} color="#ef4444" />
        <SCKpi label={lang === 'th' ? 'สถานะ (SPI)' : 'SPI'} value={spi ? spi.toFixed(2) : '—'}
          color={variance >= 0 ? 'var(--emerald, #22c55e)' : 'var(--rose, #f43)'}
          hint={variance >= 0 ? (lang === 'th' ? `เร็วกว่าแผน ${variance.toFixed(1)}%` : `+${variance.toFixed(1)}%`) : (lang === 'th' ? `ช้ากว่าแผน ${Math.abs(variance).toFixed(1)}%` : `${variance.toFixed(1)}%`)} />
      </div>

      {/* SINGLE PAGE: table (left) + chart (right) */}
      <div className="scurve-split" style={{ display: 'grid', gridTemplateColumns: 'minmax(0, 1.1fr) minmax(0, 1fr)', gap: 14, alignItems: 'start' }}>
        {/* LEFT — activity table (Plan/Action weeks + actual %) */}
        <div style={{ minWidth: 0 }}>
          <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: 6 }}>
            <div className="micro">📋 {lang === 'th' ? 'ตารางกิจกรรม (ถ่วงน้ำหนักตามมูลค่า)' : 'Activities'}</div>
            <div style={{ fontSize: 11, color: 'var(--ink-mute)' }}>
              {totalAmount > 0 ? `${lang === 'th' ? 'มูลค่ารวม' : 'Total'} ${_fmtShort(totalAmount)}` : `${lang === 'th' ? 'รวมน้ำหนัก' : 'Weight'} ${weightSum.toFixed(0)}%`}
            </div>
          </div>
          <div style={{ overflowX: 'auto', maxHeight: 380, overflowY: 'auto', border: '1px solid var(--line)', borderRadius: 8 }}>
            <table className="table" style={{ fontSize: 11, minWidth: 520 }}>
              <thead style={{ position: 'sticky', top: 0, background: 'var(--bg-1)', zIndex: 1 }}><tr>
                <th style={{ minWidth: 110 }}>{lang === 'th' ? 'กิจกรรม' : 'Activity'}</th>
                <th style={{ width: 92, textAlign: 'right' }}>{lang === 'th' ? 'มูลค่า' : 'Amount'}</th>
                <th style={{ width: 50, textAlign: 'right' }}>%</th>
                <th style={{ width: 88, textAlign: 'center' }}>Plan</th>
                <th style={{ width: 88, textAlign: 'center' }}>Action</th>
                <th style={{ width: 64, textAlign: 'center' }}>%{lang === 'th' ? 'จริง' : 'act'}</th>
                <th style={{ width: 24 }}></th>
              </tr></thead>
              <tbody>
                {activities.map((a, i) => (
                  <tr key={a.id}>
                    <td><input value={a.name} onChange={(e) => updateAct(a.id, 'name', e.target.value)} style={scInp} /></td>
                    <td><input type="number" value={a.amount || 0} onChange={(e) => updateAct(a.id, 'amount', Number(e.target.value))} style={{ ...scInp, textAlign: 'right' }} /></td>
                    <td className="mono" style={{ textAlign: 'right', color: 'var(--ink-mute)' }}>{W[i].toFixed(1)}</td>
                    <td style={{ textAlign: 'center', whiteSpace: 'nowrap' }}>
                      <input type="number" min="1" max={weeks} value={a.m_start} onChange={(e) => updateAct(a.id, 'm_start', Number(e.target.value))} style={scMini} />–
                      <input type="number" min="1" max={weeks} value={a.m_end} onChange={(e) => updateAct(a.id, 'm_end', Number(e.target.value))} style={scMini} />
                    </td>
                    <td style={{ textAlign: 'center', whiteSpace: 'nowrap' }}>
                      <input type="number" min="1" max={weeks} value={a.a_start || a.m_start} onChange={(e) => updateAct(a.id, 'a_start', Number(e.target.value))} style={scMini} />–
                      <input type="number" min="1" max={weeks} value={a.a_end || a.m_end} onChange={(e) => updateAct(a.id, 'a_end', Number(e.target.value))} style={scMini} />
                    </td>
                    <td style={{ textAlign: 'center' }}>
                      <input type="number" min="0" max="100" value={a.actual_pct || 0} onChange={(e) => updateAct(a.id, 'actual_pct', Math.min(100, Math.max(0, Number(e.target.value))))}
                        style={{ ...scInp, textAlign: 'right', color: '#ef4444', fontWeight: 700 }} />
                    </td>
                    <td><button onClick={() => removeAct(a.id)} className="btn btn-sm btn-ghost" style={{ color: 'var(--rose)', padding: '0 4px' }}>×</button></td>
                  </tr>
                ))}
              </tbody>
            </table>
          </div>
          <div style={{ display: 'flex', gap: 8, marginTop: 8, alignItems: 'center', flexWrap: 'wrap' }}>
            <button onClick={addAct} className="btn btn-sm">+ {lang === 'th' ? 'เพิ่มกิจกรรม' : 'Add'}</button>
            <span style={{ fontSize: 10.5, color: 'var(--ink-mute)' }}>
              {lang === 'th' ? 'น้ำหนัก = มูลค่า/รวม · Plan/Action = สัปดาห์เริ่ม–จบ' : 'weight = amount share · Plan/Action = start–end week'}
            </span>
          </div>

          {/* weekly snapshot save */}
          <div style={{ marginTop: 12, padding: 10, background: 'var(--bg-2)', borderRadius: 8 }}>
            <div className="micro" style={{ marginBottom: 6 }}>✍️ {lang === 'th' ? 'บันทึกผลงานจริงสะสม (รายสัปดาห์)' : 'Save weekly actual'}</div>
            <SnapSave code={code} weightedActual={weightedActual} onChange={() => setV(x => x + 1)} lang={lang} toast={toast} />
            {snapshots.length > 0 && (
              <div style={{ marginTop: 8, display: 'flex', flexWrap: 'wrap', gap: 4 }}>
                {snapshots.slice().reverse().map(s => (
                  <span key={s.date} style={{ display: 'inline-flex', alignItems: 'center', gap: 4, fontSize: 10.5, padding: '2px 6px', background: 'var(--bg-1, var(--glass-3))', borderRadius: 10 }}>
                    <span className="mono" style={{ color: 'var(--ink-mute)' }}>W{_isoWeekIndex(start_date, s.date)}</span>
                    <span className="mono" style={{ color: '#ef4444', fontWeight: 700 }}>{s.cum_pct}%</span>
                    <button onClick={() => { window.SCurveManager.removeSnapshot(code, s.date); setV(x => x + 1); }} style={{ border: 'none', background: 'none', color: 'var(--rose)', cursor: 'pointer', padding: 0 }}>×</button>
                  </span>
                ))}
              </div>
            )}
          </div>
        </div>

        {/* RIGHT — chart */}
        <div style={{ minWidth: 0 }}>
          <SCurveSVG weeks={weeks} master={masterCurve} action={actionCurve} actualPts={actualPts} currentWeek={currentWeek} lang={lang} />
        </div>
      </div>
    </div>
  );
}

// compact snapshot saver
function SnapSave({ code, weightedActual, onChange, lang, toast }) {
  const today = new Date().toISOString().split('T')[0];
  const [date, setDate] = React.useState(today);
  const [pct, setPct] = React.useState(weightedActual);
  React.useEffect(() => { setPct(weightedActual); }, [weightedActual]);
  return (
    <div style={{ display: 'flex', gap: 8, alignItems: 'flex-end', flexWrap: 'wrap' }}>
      <div>
        <div className="micro" style={{ marginBottom: 2 }}>{lang === 'th' ? 'วันที่' : 'Date'}</div>
        <input type="date" value={date} max={today} onChange={(e) => setDate(e.target.value)} style={{ ...scInp, width: 130 }} />
      </div>
      <div>
        <div className="micro" style={{ marginBottom: 2 }}>{lang === 'th' ? '% สะสมจริง' : 'Cum %'}</div>
        <input type="number" min="0" max="100" value={pct} onChange={(e) => setPct(Number(e.target.value))} style={{ ...scInp, width: 80, textAlign: 'right', fontWeight: 700 }} />
      </div>
      <span style={{ fontSize: 10, color: 'var(--ink-mute)', paddingBottom: 7 }}>{lang === 'th' ? `ถ่วงน้ำหนัก=${weightedActual}%` : `wtd=${weightedActual}%`}</span>
      <button onClick={() => { if (!date) return; window.SCurveManager.addSnapshot(code, { date, cum_pct: Number(pct), note: '' }); onChange(); toast(lang === 'th' ? `✓ บันทึก W${date}` : 'Saved', 'success'); }}
        className="btn btn-sm btn-primary" style={{ background: 'var(--gold-soft, #c89858)', color: '#1a1208', border: 'none' }}>💾</button>
    </div>
  );
}

function SCKpi({ label, value, color, hint }) {
  return (
    <div style={{ padding: '8px 10px', background: 'var(--bg-2)', borderRadius: 8 }}>
      <div style={{ fontSize: 9.5, color: 'var(--ink-mute)', textTransform: 'uppercase', letterSpacing: '.04em' }}>{label}</div>
      <div className="mono" style={{ fontSize: 15, fontWeight: 700, color: color || 'var(--ink)', marginTop: 1 }}>{value}</div>
      {hint && <div style={{ fontSize: 9.5, color: 'var(--ink-mute)' }}>{hint}</div>}
    </div>
  );
}

// ─── Actual weekly entry ───────────────────────────────────────
function SCurveActualEntry({ code, startDate, weeks, activities, weightedActual, updateAct, snapshots, onChange, lang, toast }) {
  const today = new Date().toISOString().split('T')[0];
  const [snapDate, setSnapDate] = React.useState(today);
  const [snapPct, setSnapPct] = React.useState(weightedActual);

  React.useEffect(() => { setSnapPct(weightedActual); }, [weightedActual]);

  const saveSnap = () => {
    if (!snapDate) { toast(lang === 'th' ? 'เลือกวันที่' : 'Pick date', 'warning'); return; }
    window.SCurveManager.addSnapshot(code, { date: snapDate, cum_pct: Number(snapPct), note: '' });
    onChange();
    toast(lang === 'th' ? `✓ บันทึกผลสัปดาห์ ${snapDate} = ${snapPct}%` : 'Snapshot saved', 'success');
  };

  return (
    <div>
      {/* per-activity % → weighted */}
      <div className="micro" style={{ marginBottom: 6 }}>{lang === 'th' ? '1) กรอก % แล้วเสร็จจริงของแต่ละกิจกรรม → ระบบถ่วงน้ำหนักให้' : '1) Enter % complete per activity'}</div>
      <div style={{ overflowX: 'auto', marginBottom: 12 }}>
        <table className="table" style={{ fontSize: 11.5, minWidth: 420 }}>
          <thead><tr>
            <th>{lang === 'th' ? 'กิจกรรม' : 'Activity'}</th>
            <th style={{ width: 80, textAlign: 'right' }}>{lang === 'th' ? 'น้ำหนัก%' : 'Weight%'}</th>
            <th style={{ width: 120, textAlign: 'center' }}>{lang === 'th' ? '% แล้วเสร็จ' : '% done'}</th>
          </tr></thead>
          <tbody>
            {activities.map(a => (
              <tr key={a.id}>
                <td>{a.name}</td>
                <td className="mono" style={{ textAlign: 'right' }}>{a.weight}%</td>
                <td style={{ textAlign: 'center' }}>
                  <input type="number" min="0" max="100" value={a.actual_pct || 0}
                    onChange={(e) => updateAct(a.id, 'actual_pct', Math.min(100, Math.max(0, Number(e.target.value))))}
                    style={{ ...scInp, textAlign: 'right', width: 90 }} />
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      {/* snapshot save */}
      <div style={{ padding: 12, background: 'var(--bg-2)', borderRadius: 8, marginBottom: 12 }}>
        <div className="micro" style={{ marginBottom: 8 }}>{lang === 'th' ? '2) บันทึกเป็นจุดสะสมรายสัปดาห์ (snapshot)' : '2) Save weekly snapshot'}</div>
        <div style={{ display: 'flex', gap: 10, alignItems: 'flex-end', flexWrap: 'wrap' }}>
          <div>
            <div className="micro" style={{ marginBottom: 4 }}>{lang === 'th' ? 'วันที่ (สิ้นสัปดาห์)' : 'Date'}</div>
            <input type="date" value={snapDate} max={today} onChange={(e) => setSnapDate(e.target.value)} style={scInp} />
          </div>
          <div>
            <div className="micro" style={{ marginBottom: 4 }}>{lang === 'th' ? '% สะสมจริง' : 'Cum %'}</div>
            <input type="number" min="0" max="100" value={snapPct} onChange={(e) => setSnapPct(Number(e.target.value))} style={{ ...scInp, width: 110, textAlign: 'right', fontWeight: 700 }} />
          </div>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)', paddingBottom: 8 }}>
            {lang === 'th' ? `(จากถ่วงน้ำหนัก = ${weightedActual}%)` : `(weighted = ${weightedActual}%)`}
          </div>
          <button onClick={saveSnap} className="btn btn-primary btn-sm" style={{ background: 'var(--gold-soft, #c89858)', color: '#1a1208', border: 'none' }}>
            💾 {lang === 'th' ? 'บันทึกผลสัปดาห์' : 'Save'}
          </button>
        </div>
      </div>

      {/* snapshot list */}
      {snapshots.length > 0 && (
        <div>
          <div className="micro" style={{ marginBottom: 6 }}>{lang === 'th' ? `ประวัติผลรายสัปดาห์ (${snapshots.length})` : `History (${snapshots.length})`}</div>
          <div style={{ display: 'flex', flexDirection: 'column', gap: 4 }}>
            {snapshots.slice().reverse().map(s => (
              <div key={s.date} style={{ display: 'flex', alignItems: 'center', gap: 10, padding: '6px 10px', background: 'var(--bg-2)', borderRadius: 6, fontSize: 12 }}>
                <span className="mono" style={{ color: 'var(--ink-mute)' }}>W{_isoWeekIndex(startDate, s.date)}</span>
                <span className="mono">{s.date}</span>
                <span style={{ flex: 1 }} />
                <span className="mono" style={{ fontWeight: 700, color: '#ef4444' }}>{s.cum_pct}%</span>
                <button onClick={() => { window.SCurveManager.removeSnapshot(code, s.date); onChange(); }} className="btn btn-sm btn-ghost" style={{ color: 'var(--rose)', padding: '0 6px' }}>×</button>
              </div>
            ))}
          </div>
        </div>
      )}
    </div>
  );
}

// ─── SVG chart: Master(dashed blue) + Action(solid blue) + Actual(red) ──
function SCurveSVG({ weeks, master, action, actualPts, currentWeek, lang }) {
  const W = 900, H = 320, padL = 44, padR = 24, padT = 18, padB = 38;
  const plotW = W - padL - padR, plotH = H - padT - padB;
  const x = (wk) => padL + ((wk - 1) / Math.max(1, weeks - 1)) * plotW;
  const y = (pct) => padT + plotH - (pct / 100) * plotH;
  const path = (arr) => arr.map((p, i) => `${i ? 'L' : 'M'} ${x(i + 1).toFixed(1)} ${y(p).toFixed(1)}`).join(' ');
  const actualPath = actualPts.length ? `M ${x(1).toFixed(1)} ${y(0)} ` + actualPts.map(p => `L ${x(p.week).toFixed(1)} ${y(p.pct).toFixed(1)}`).join(' ') : '';
  const labelEvery = Math.ceil(weeks / 12);

  return (
    <div style={{ overflowX: 'auto' }}>
      <svg viewBox={`0 0 ${W} ${H}`} style={{ width: '100%', minWidth: 600, height: 'auto' }}>
        {[0, 25, 50, 75, 100].map(p => (
          <g key={p}>
            <line x1={padL} y1={y(p)} x2={W - padR} y2={y(p)} stroke="var(--line)" strokeWidth="1" />
            <text x={padL - 6} y={y(p) + 4} textAnchor="end" fontSize="10" fill="var(--ink-mute)">{p}%</text>
          </g>
        ))}
        {Array.from({ length: weeks }, (_, i) => i + 1).filter(w => w === 1 || w === weeks || w % labelEvery === 0).map(w => (
          <text key={w} x={x(w)} y={H - padB + 16} textAnchor="middle" fontSize="9" fill="var(--ink-mute)">W{w}</text>
        ))}
        {/* today */}
        {currentWeek >= 1 && currentWeek <= weeks && (
          <g>
            <line x1={x(currentWeek)} y1={padT} x2={x(currentWeek)} y2={padT + plotH} stroke="#fbbf24" strokeWidth="1" strokeDasharray="4 3" />
            <text x={x(currentWeek)} y={padT - 5} textAnchor="middle" fontSize="9" fill="#fbbf24" fontWeight="700">{lang === 'th' ? 'วันนี้' : 'now'}</text>
          </g>
        )}
        {/* Master plan — dashed blue */}
        <path d={path(master)} fill="none" stroke="#6f86ff" strokeWidth="1.8" strokeDasharray="6 4" opacity="0.7" />
        {/* Action plan — solid blue */}
        <path d={path(action)} fill="none" stroke="#6f86ff" strokeWidth="2.5" />
        {/* Actual — red */}
        {actualPath && <path d={actualPath} fill="none" stroke="#ef4444" strokeWidth="2.5" />}
        {actualPts.map((p, i) => (
          <g key={i}>
            <circle cx={x(p.week)} cy={y(p.pct)} r="3.5" fill="#ef4444" />
            <text x={x(p.week)} y={y(p.pct) - 7} textAnchor="middle" fontSize="8.5" fill="#ef4444" fontWeight="700">{p.pct}%</text>
          </g>
        ))}
        {/* legend */}
        <g transform={`translate(${padL + 8}, ${padT + 6})`} fontSize="9.5">
          <line x1="0" y1="0" x2="18" y2="0" stroke="#6f86ff" strokeWidth="1.8" strokeDasharray="6 4" /><text x="22" y="3" fill="var(--ink-soft)">Plan</text>
          <line x1="62" y1="0" x2="80" y2="0" stroke="#6f86ff" strokeWidth="2.5" /><text x="84" y="3" fill="var(--ink-soft)">Action</text>
          <line x1="140" y1="0" x2="158" y2="0" stroke="#ef4444" strokeWidth="2.5" /><text x="162" y="3" fill="var(--ink-soft)">{lang === 'th' ? 'จริง' : 'Actual'}</text>
        </g>
      </svg>
    </div>
  );
}

const scInp = { width: '100%', padding: '6px 8px', background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 5, color: 'var(--ink)', fontSize: 11.5, fontFamily: 'inherit' };
const scMini = { width: 38, padding: '4px 4px', background: 'var(--bg-2)', border: '1px solid var(--line)', borderRadius: 4, color: 'var(--ink)', fontSize: 11, textAlign: 'center', margin: '0 2px', fontFamily: 'var(--font-mono)' };

window.WeeklySCurvePanel = WeeklySCurvePanel;
