// Cost Analysis Dashboard — Budget Control + Variance + Cost Breakdown
// Inspired by user's profit-management vision

function CostAnalysisPage({ lang }) {
  const [project, setProject] = useState(null);
  const [spend, setSpend] = useState(null);
  const [variance, setVariance] = useState([]);
  const [budgets, setBudgets] = useState([]);
  const [loading, setLoading] = useState(false);
  const [tab, setTab] = useState('overview'); // overview | breakdown | variance | budgets
  const toast = useToast();

  const reload = useCallback(async (pid) => {
    if (!pid) return;
    setLoading(true);
    try {
      if (window.SYK_MODE === 'web' && window.api) {
        const [sp, vr, bg] = await Promise.all([
          window.api.costSpend(pid).catch(() => null),
          window.api.costVariance(pid).catch(() => []),
          window.api.costBudgets(pid).catch(() => []),
        ]);
        if (sp && (sp.rows || []).length === 0) {
          // No data yet → mock for showcase
          setSpend(generateMockSpend());
          setVariance(generateMockVariance());
          setBudgets(generateMockBudgets());
        } else {
          setSpend(sp);
          setVariance(vr || []);
          setBudgets(bg || []);
        }
      } else {
        setSpend(generateMockSpend());
        setVariance(generateMockVariance());
        setBudgets(generateMockBudgets());
      }
    } finally { setLoading(false); }
  }, []);

  useEffect(() => {
    if (PROJECTS.length && !project) {
      const first = PROJECTS[0];
      setProject(first);
      reload(first.id || 1);
    }
  }, [reload, project]);

  // Auto-seed master cost codes to D1 on first visit (idempotent)
  useEffect(() => {
    if (window.SYK_MODE === 'web' && window.api && window.MASTER_COST_CODES) {
      window.api.costCodeSeedMaster(window.MASTER_COST_CODES)
        .then(r => console.log('[Cost Code] Seeded master:', r.inserted))
        .catch(() => {});
    }
  }, []);

  if (!project || !spend) return <div className="card" style={{ textAlign: 'center', padding: 60 }}>กำลังโหลด...</div>;

  const t = spend.totals;
  const overBudget = (spend.alerts || []).filter(a => a.severity === 'critical').length;
  const warningBudget = (spend.alerts || []).filter(a => a.severity === 'warning').length;

  return (
    <div className="anim-fadein">
      <PageHeader
        title={lang === 'th' ? 'วิเคราะห์ต้นทุน (Cost Analysis)' : 'Cost Analysis'}
        subtitle={lang === 'th'
          ? 'ผูก Cost Code กับ PR/PO + Invoice → ควบคุมงบประมาณแบบ real-time'
          : 'Real-time budget control & variance analysis'}
        actions={<>
          <select className="field" style={{ width: 240, height: 36, fontSize: 12 }}
            value={project?.id || ''}
            onChange={(e) => {
              const p = PROJECTS.find(x => String(x.id) === e.target.value);
              if (p) { setProject(p); reload(p.id); }
            }}>
            {PROJECTS.map(p => <option key={p.id || p.code} value={p.id || p.code}>{p.code} — {p.name}</option>)}
          </select>
          <button className="btn btn-sm" onClick={async () => {
            const boqId = prompt('BOQ ID:');
            if (boqId && window.api) {
              const r = await window.api.costBudgetsFromBOQ(boqId).catch(e => ({ error: e.message }));
              toast(r.error || `สร้าง budget แล้ว ${r.created} รายการ`, r.error ? 'danger' : 'success');
              reload(project.id);
            }
          }}><I.plus /> {lang === 'th' ? 'ดึง Budget จาก BOQ' : 'From BOQ'}</button>
        </>}
      />

      {/* TOP KPIs */}
      <div style={{ display: 'grid', gridTemplateColumns: 'repeat(5, 1fr)', gap: 10, marginBottom: 14 }}>
        <KpiCard label="งบประมาณรวม" value={fmtTHBshort(t.allocated)} accent="#6f86ff"
          hint={`${(spend.rows || []).length} cost codes`} />
        <KpiCard label="ผูกแล้ว (PO)" value={fmtTHBshort(t.committed)} accent="#f6c97c"
          hint={`${t.utilization_pct.toFixed(1)}% utilization`} />
        <KpiCard label="จ่ายจริง (Invoice)" value={fmtTHBshort(t.paid)} accent="#4ade80"
          hint={`${t.allocated > 0 ? ((t.paid/t.allocated)*100).toFixed(1) : 0}%`} />
        <KpiCard label="เหลือใช้ได้" value={fmtTHBshort(t.remaining)} accent={t.remaining >= 0 ? '#4ade80' : '#ff6b8a'}
          hint={t.remaining >= 0 ? 'on budget' : `เกินงบ ${fmtTHBshort(Math.abs(t.remaining))}`} />
        <KpiCard label="แจ้งเตือน" value={overBudget + warningBudget}
          accent={overBudget > 0 ? '#ff6b8a' : warningBudget > 0 ? '#fbbf24' : '#4ade80'}
          hint={overBudget > 0 ? `${overBudget} critical` : warningBudget > 0 ? `${warningBudget} warning` : 'ปกติ'} />
      </div>

      {/* Tabs */}
      <div style={{ display: 'flex', gap: 6, marginBottom: 14, padding: 4, background: 'var(--glass)', border: '1px solid var(--line)', borderRadius: 12, width: 'fit-content' }}>
        {[
          { id: 'overview', th: '📊 ภาพรวม', en: 'Overview' },
          { id: 'breakdown', th: '🥧 แยกหมวด', en: 'Breakdown' },
          { id: 'variance', th: '📈 Variance', en: 'Variance' },
          { id: 'budgets', th: '📋 จัดการงบ', en: 'Budgets' },
        ].map(tt => (
          <button key={tt.id} onClick={() => setTab(tt.id)}
            style={{ padding: '8px 14px', borderRadius: 8, border: 0, cursor: 'pointer',
                     background: tab === tt.id ? 'var(--syk-blue)' : 'transparent',
                     color: tab === tt.id ? 'white' : 'var(--ink-soft)',
                     fontSize: 12.5, fontWeight: 500 }}>
            {lang === 'th' ? tt.th : tt.en}
          </button>
        ))}
      </div>

      {/* ALERTS — always visible */}
      {(spend.alerts || []).length > 0 && (
        <div className="card" style={{ marginBottom: 14, background: 'rgba(255,107,138,0.05)', borderColor: 'rgba(255,107,138,0.3)' }}>
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 10 }}>
            <span style={{ fontSize: 16 }}>🚨</span>
            <h3 className="h2" style={{ margin: 0 }}>Budget Alerts ({spend.alerts.length})</h3>
          </div>
          <div style={{ display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 8 }}>
            {spend.alerts.slice(0, 6).map((a, i) => (
              <div key={i} style={{
                padding: '8px 12px',
                background: a.severity === 'critical' ? 'rgba(255,107,138,0.1)' : 'rgba(251,191,36,0.1)',
                borderLeft: `3px solid ${a.severity === 'critical' ? 'var(--rose)' : 'var(--amber)'}`,
                borderRadius: 6, fontSize: 12,
              }}>
                <div style={{ fontWeight: 600, color: a.severity === 'critical' ? 'var(--rose)' : 'var(--amber)' }}>
                  {a.severity === 'critical' ? '🔴' : '🟡'} {a.title}
                </div>
                <div style={{ fontSize: 10.5, color: 'var(--ink-mute)', marginTop: 2 }}>
                  ใช้ {fmtTHBshort(a.committed)} / {fmtTHBshort(a.allocated)}
                </div>
              </div>
            ))}
          </div>
        </div>
      )}

      {/* TAB: OVERVIEW */}
      {tab === 'overview' && (
        <>
          {/* Top 5 Cost Drivers */}
          <div className="card" style={{ marginBottom: 14 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
              <span style={{ fontSize: 16 }}>🏆</span>
              <h3 className="h2" style={{ margin: 0 }}>Top 5 Cost Drivers</h3>
            </div>
            {(spend.top5 || []).map((r, i) => (
              <BudgetBar key={r.cost_code} idx={i+1} row={r} maxVal={t.allocated} />
            ))}
          </div>

          {/* M vs L Ratio */}
          <div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr', gap: 14, marginBottom: 14 }}>
            <div className="card">
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
                <span style={{ fontSize: 16 }}>⚖️</span>
                <h3 className="h2" style={{ margin: 0 }}>Material vs Labor vs Equipment Ratio</h3>
              </div>
              <MLERatio data={spend.matVsLab || {}} />
            </div>
            <div className="card">
              <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
                <span style={{ fontSize: 16 }}>📊</span>
                <h3 className="h2" style={{ margin: 0 }}>Project Utilization</h3>
              </div>
              <UtilizationDonut totals={t} />
            </div>
          </div>
        </>
      )}

      {/* TAB: BREAKDOWN */}
      {tab === 'breakdown' && (
        <div className="card">
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
            <span style={{ fontSize: 16 }}>🥧</span>
            <h3 className="h2" style={{ margin: 0 }}>Cost Breakdown by Category</h3>
          </div>
          {(spend.byCategory || []).map(cat => (
            <CategoryBlock key={cat.category_code} cat={cat} maxVal={t.allocated} />
          ))}
        </div>
      )}

      {/* TAB: VARIANCE */}
      {tab === 'variance' && (
        <div className="card">
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
            <span style={{ fontSize: 16 }}>📈</span>
            <h3 className="h2" style={{ margin: 0 }}>BOQ Rate vs Market Rate (Variance Analysis)</h3>
          </div>
          {variance.length === 0 ? (
            <div style={{ padding: 30, textAlign: 'center', color: 'var(--ink-mute)' }}>
              ยังไม่มีใบเสนอราคา supplier — เพิ่มใบเสนอราคาเพื่อเปรียบเทียบ
            </div>
          ) : (
            <table className="table">
              <thead><tr>
                <th>Cost Code</th>
                <th>รายการ</th>
                <th style={{ textAlign: 'right' }}>ราคา BOQ</th>
                <th style={{ textAlign: 'right' }}>ราคาดีสุด (Best Quote)</th>
                <th style={{ textAlign: 'right' }}>Variance</th>
                <th style={{ textAlign: 'right' }}>Saving Potential</th>
              </tr></thead>
              <tbody>
                {variance.map(r => (
                  <tr key={r.cost_code} className="row-int">
                    <td className="mono"><strong>{r.cost_code}</strong></td>
                    <td style={{ fontSize: 12 }}>{r.notes || '—'}</td>
                    <td className="mono" style={{ textAlign: 'right' }}>{fmtTHB(r.boq_rate)}</td>
                    <td className="mono" style={{ textAlign: 'right', color: 'var(--syk-blue-soft)' }}>{fmtTHB(r.best_quote)}</td>
                    <td className="mono" style={{ textAlign: 'right', fontWeight: 600,
                          color: r.best_variance_pct < 0 ? 'var(--emerald)' : 'var(--rose)' }}>
                      {r.best_variance_pct > 0 ? '+' : ''}{r.best_variance_pct.toFixed(1)}%
                    </td>
                    <td className="mono" style={{ textAlign: 'right', color: r.saving_potential > 0 ? 'var(--emerald)' : 'var(--ink-mute)' }}>
                      {r.saving_potential > 0 ? '+' : ''}{fmtTHB(r.saving_potential)}
                    </td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      )}

      {/* TAB: BUDGETS */}
      {tab === 'budgets' && (
        <div className="card">
          <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 14 }}>
            <h3 className="h2" style={{ flex: 1, margin: 0 }}>Budget Allocation ({budgets.length})</h3>
          </div>
          {budgets.length === 0 ? (
            <div style={{ padding: 30, textAlign: 'center', color: 'var(--ink-mute)' }}>
              <p>ยังไม่ได้จัดสรรงบประมาณตาม Cost Code</p>
              <p style={{ fontSize: 11, marginTop: 8 }}>กดปุ่ม "ดึง Budget จาก BOQ" เพื่อสร้างอัตโนมัติ</p>
            </div>
          ) : (
            <table className="table">
              <thead><tr>
                <th>Cost Code</th><th>รายการ</th>
                <th style={{ textAlign: 'right' }}>ปริมาณ</th>
                <th>หน่วย</th>
                <th style={{ textAlign: 'right' }}>ราคา/หน่วย</th>
                <th style={{ textAlign: 'right' }}>งบประมาณ</th>
                <th></th>
              </tr></thead>
              <tbody>
                {budgets.map(b => (
                  <tr key={b.id} className="row-int">
                    <td className="mono"><strong>{b.cost_code}</strong></td>
                    <td style={{ fontSize: 12, maxWidth: 280, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{b.notes || '—'}</td>
                    <td className="mono" style={{ textAlign: 'right' }}>{b.allocated_qty}</td>
                    <td className="ink-soft">{b.unit || '—'}</td>
                    <td className="mono" style={{ textAlign: 'right' }}>{fmtTHB(b.allocated_rate)}</td>
                    <td className="mono" style={{ textAlign: 'right', fontWeight: 600 }}>{fmtTHB(b.allocated_amount)}</td>
                    <td><button className="btn btn-sm btn-ghost" style={{ color: 'var(--rose)' }}
                      onClick={async () => {
                        if (!confirm('ลบ budget นี้?')) return;
                        if (window.api) { await window.api.costBudgetDelete(b.id); reload(project.id); }
                      }}><I.trash /></button></td>
                  </tr>
                ))}
              </tbody>
            </table>
          )}
        </div>
      )}
    </div>
  );
}

// ─────────────────────────────────────────────────────────────
// COMPONENTS
// ─────────────────────────────────────────────────────────────
function KpiCard({ label, value, accent, hint }) {
  return (
    <div className="card card-tight" style={{
      borderTop: `3px solid ${accent}`,
      background: `linear-gradient(180deg, ${accent}11 0%, transparent 80%)`,
    }}>
      <div className="micro" style={{ fontSize: 9 }}>{label}</div>
      <div className="mono" style={{ fontSize: 18, fontWeight: 800, marginTop: 4, color: accent }}>{value}</div>
      {hint && <div style={{ fontSize: 10, color: 'var(--ink-mute)', marginTop: 2 }}>{hint}</div>}
    </div>
  );
}

function BudgetBar({ idx, row, maxVal }) {
  const allocatedW = maxVal > 0 ? Math.min(100, (row.allocated / maxVal) * 100) : 0;
  const committedW = row.allocated > 0 ? Math.min(100, (row.committed / row.allocated) * 100) : 0;
  const overBudget = row.committed > row.allocated;
  const color = overBudget ? '#ff6b8a' : committedW > 90 ? '#fbbf24' : '#4ade80';

  return (
    <div style={{ marginBottom: 14, padding: 10, background: 'var(--glass-2)', borderRadius: 8 }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 8 }}>
        <span style={{ width: 24, height: 24, borderRadius: 6, background: idx === 1 ? '#fbbf24' : 'var(--glass-3)',
                       display: 'inline-flex', alignItems: 'center', justifyContent: 'center', fontWeight: 700, fontSize: 11 }}>{idx}</span>
        <span className="mono" style={{ fontWeight: 700, fontSize: 12, color: 'var(--syk-blue-soft)' }}>{row.cost_code}</span>
        <span style={{ flex: 1, fontSize: 12, color: 'var(--ink-soft)', overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }}>{row.description || '—'}</span>
        <span className="mono" style={{ fontWeight: 600, fontSize: 11.5 }}>{fmtTHBshort(row.committed)} / {fmtTHBshort(row.allocated)}</span>
        <span style={{ fontSize: 11, fontWeight: 700, color, minWidth: 60, textAlign: 'right' }}>
          {row.variance_pct > 0 ? '+' : ''}{row.variance_pct.toFixed(1)}%
        </span>
      </div>
      <div style={{ height: 8, background: 'rgba(255,255,255,0.05)', borderRadius: 999, overflow: 'hidden', position: 'relative' }}>
        <div style={{ height: '100%', width: committedW + '%', background: color, borderRadius: 999 }} />
        {/* 100% marker line */}
        <div style={{ position: 'absolute', left: '100%', top: 0, bottom: 0, width: 1, background: 'rgba(255,255,255,0.3)' }} />
      </div>
    </div>
  );
}

function MLERatio({ data }) {
  const totalAllocated = (data.M?.allocated || 0) + (data.L?.allocated || 0) + (data.E?.allocated || 0);
  const totalCommitted = (data.M?.committed || 0) + (data.L?.committed || 0) + (data.E?.committed || 0);
  if (totalAllocated === 0) return <div style={{ padding: 20, textAlign: 'center', color: 'var(--ink-mute)' }}>ยังไม่มีข้อมูล</div>;
  const items = [
    { type: 'M', label: 'วัสดุ (Material)', icon: '🧱', color: '#a78bfa' },
    { type: 'L', label: 'ค่าแรง (Labor)', icon: '👷', color: '#f472b6' },
    { type: 'E', label: 'เครื่องจักร (Equipment)', icon: '🚜', color: '#fbbf24' },
  ];
  return (
    <div>
      {items.map(it => {
        const d = data[it.type] || { allocated: 0, committed: 0 };
        const allocPct = totalAllocated > 0 ? (d.allocated / totalAllocated) * 100 : 0;
        const utilPct = d.allocated > 0 ? (d.committed / d.allocated) * 100 : 0;
        return (
          <div key={it.type} style={{ marginBottom: 14 }}>
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 6 }}>
              <span style={{ fontSize: 16 }}>{it.icon}</span>
              <span style={{ fontWeight: 600, fontSize: 13, flex: 1 }}>{it.label}</span>
              <span className="mono" style={{ fontSize: 12, color: it.color, fontWeight: 700 }}>{allocPct.toFixed(1)}%</span>
            </div>
            <div style={{ height: 18, background: 'rgba(255,255,255,0.05)', borderRadius: 6, overflow: 'hidden', position: 'relative' }}>
              <div style={{ height: '100%', width: allocPct + '%', background: it.color + '40', position: 'absolute', left: 0 }} />
              <div style={{ height: '100%', width: ((d.committed / totalAllocated) * 100) + '%', background: it.color, position: 'absolute', left: 0 }} />
            </div>
            <div style={{ display: 'flex', justifyContent: 'space-between', fontSize: 10.5, color: 'var(--ink-mute)', marginTop: 4 }}>
              <span>Allocated: {fmtTHB(d.allocated)}</span>
              <span>Committed: {fmtTHB(d.committed)} ({utilPct.toFixed(0)}%)</span>
            </div>
          </div>
        );
      })}
    </div>
  );
}

function UtilizationDonut({ totals }) {
  const pct = totals.utilization_pct || 0;
  const radius = 70, stroke = 14, c = 2 * Math.PI * radius;
  const dash = (Math.min(100, pct) / 100) * c;
  const color = pct > 100 ? '#ff6b8a' : pct > 80 ? '#fbbf24' : '#4ade80';
  return (
    <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
      <svg width={radius*2 + stroke + 10} height={radius*2 + stroke + 10}>
        <circle cx={radius + stroke/2 + 5} cy={radius + stroke/2 + 5} r={radius}
          fill="none" stroke="rgba(255,255,255,0.08)" strokeWidth={stroke} />
        <circle cx={radius + stroke/2 + 5} cy={radius + stroke/2 + 5} r={radius}
          fill="none" stroke={color} strokeWidth={stroke} strokeLinecap="round"
          strokeDasharray={`${dash} ${c}`}
          transform={`rotate(-90 ${radius + stroke/2 + 5} ${radius + stroke/2 + 5})`} />
        <text x={radius + stroke/2 + 5} y={radius + stroke/2 + 5} textAnchor="middle" dy="-2"
          fontSize="22" fontWeight="700" fill="var(--ink)" fontFamily="var(--font-mono)">{pct.toFixed(0)}%</text>
        <text x={radius + stroke/2 + 5} y={radius + stroke/2 + 5} textAnchor="middle" dy="18"
          fontSize="10" fill="var(--ink-mute)">Committed</text>
      </svg>
      <div style={{ fontSize: 11.5, color: 'var(--ink-soft)', textAlign: 'center', lineHeight: 1.7 }}>
        <strong>{fmtTHBshort(totals.committed)}</strong> ผูกแล้ว<br />
        จากงบ <strong>{fmtTHBshort(totals.allocated)}</strong><br />
        เหลือใช้ <strong style={{ color: totals.remaining >= 0 ? 'var(--emerald)' : 'var(--rose)' }}>
          {fmtTHBshort(totals.remaining)}
        </strong>
      </div>
    </div>
  );
}

function CategoryBlock({ cat, maxVal }) {
  const utilPct = cat.allocated > 0 ? (cat.committed / cat.allocated) * 100 : 0;
  const overBudget = cat.committed > cat.allocated;
  const color = overBudget ? '#ff6b8a' : utilPct > 90 ? '#fbbf24' : '#4ade80';

  // Lookup category meta from MASTER_COST_CODES
  const catMeta = (window.MASTER_COST_CODES || []).find(x => x.code === cat.category_code);

  return (
    <div style={{ marginBottom: 14, padding: 14, background: 'var(--glass-2)', borderRadius: 10, border: '1px solid var(--line)' }}>
      <div style={{ display: 'flex', alignItems: 'center', gap: 10, marginBottom: 10 }}>
        <span style={{ fontSize: 22 }}>{catMeta?.icon || '📦'}</span>
        <span className="mono" style={{ fontFamily: 'var(--font-mono)', fontWeight: 700, fontSize: 14, color: 'var(--syk-blue-soft)', minWidth: 36 }}>{cat.category_code}</span>
        <div style={{ flex: 1 }}>
          <div style={{ fontWeight: 700, fontSize: 14 }}>{catMeta?.name || '—'}</div>
          <div style={{ fontSize: 11, color: 'var(--ink-mute)' }}>{catMeta?.name_en || ''}</div>
        </div>
        <div style={{ textAlign: 'right' }}>
          <div className="mono" style={{ fontSize: 13, fontWeight: 700, color }}>{utilPct.toFixed(1)}%</div>
          <div style={{ fontSize: 10.5, color: 'var(--ink-mute)' }}>{fmtTHB(cat.committed)} / {fmtTHB(cat.allocated)}</div>
        </div>
      </div>
      <div style={{ height: 6, background: 'rgba(255,255,255,0.05)', borderRadius: 999, overflow: 'hidden' }}>
        <div style={{ height: '100%', width: Math.min(100, utilPct) + '%', background: color }} />
      </div>
      <div style={{ display: 'flex', gap: 16, marginTop: 8, fontSize: 11, color: 'var(--ink-soft)' }}>
        <span>💰 จ่ายแล้ว: {fmtTHB(cat.paid)}</span>
        <span>📋 ผูกแล้ว: {fmtTHB(cat.committed)}</span>
        <span>📦 งบ: {fmtTHB(cat.allocated)}</span>
      </div>
    </div>
  );
}

// ─── Helpers ─────────────────────────────────
function fmtTHBshort(n) {
  if (!n && n !== 0) return '—';
  const sign = n < 0 ? '-' : '';
  const v = Math.abs(n);
  if (v >= 1e6) return sign + (v/1e6).toFixed(2) + 'M';
  if (v >= 1e3) return sign + (v/1e3).toFixed(0) + 'K';
  return sign + Math.round(v).toLocaleString();
}

// ─── Mock data for showcase ──────────────────
function generateMockSpend() {
  const rows = [
    { cost_code: 'M-203-001', type: 'M', category_code: '200', description: 'งานคอนกรีตฐานราก',
      allocated: 156000, allocated_qty: 24, allocated_rate: 6500, committed: 142000, paid: 110000, remaining: 14000,
      remaining_pct: 8.9, variance_pct: -8.9, status: 'ok', po_count: 2, invoice_count: 1 },
    { cost_code: 'L-203-001', type: 'L', category_code: '200', description: 'ค่าแรงคอนกรีตฐานราก',
      allocated: 60000, allocated_qty: 24, allocated_rate: 2500, committed: 65000, paid: 50000, remaining: -5000,
      remaining_pct: -8.3, variance_pct: 8.3, status: 'over', po_count: 1, invoice_count: 1 },
    { cost_code: 'M-205-003', type: 'M', category_code: '200', description: 'เหล็ก DB12',
      allocated: 285000, allocated_qty: 1000, allocated_rate: 285, committed: 260000, paid: 200000, remaining: 25000,
      remaining_pct: 8.8, variance_pct: -8.8, status: 'ok', po_count: 3, invoice_count: 2 },
    { cost_code: 'M-301-002', type: 'M', category_code: '300', description: 'อิฐมวลเบา Q-Con',
      allocated: 84000, allocated_qty: 3000, allocated_rate: 28, committed: 78000, paid: 60000, remaining: 6000,
      remaining_pct: 7.1, variance_pct: -7.1, status: 'warning', po_count: 2, invoice_count: 1 },
    { cost_code: 'L-302-001', type: 'L', category_code: '300', description: 'ค่าแรงฉาบเรียบภายใน',
      allocated: 145000, allocated_qty: 580, allocated_rate: 250, committed: 152000, paid: 120000, remaining: -7000,
      remaining_pct: -4.8, variance_pct: 4.8, status: 'over', po_count: 2, invoice_count: 2 },
    { cost_code: 'M-401-005', type: 'M', category_code: '400', description: 'ท่อ PVC 16mm',
      allocated: 24000, allocated_qty: 200, allocated_rate: 120, committed: 18000, paid: 15000, remaining: 6000,
      remaining_pct: 25, variance_pct: -25, status: 'ok', po_count: 1, invoice_count: 1 },
  ];
  const totals = {
    allocated: rows.reduce((a, r) => a + r.allocated, 0),
    committed: rows.reduce((a, r) => a + r.committed, 0),
    paid: rows.reduce((a, r) => a + r.paid, 0),
  };
  totals.remaining = totals.allocated - totals.committed;
  totals.utilization_pct = (totals.committed / totals.allocated) * 100;
  return {
    project_id: 1, totals, rows,
    top5: rows.slice(0, 5),
    byCategory: [
      { category_code: '200', allocated: 501000, committed: 467000, paid: 360000 },
      { category_code: '300', allocated: 229000, committed: 230000, paid: 180000 },
      { category_code: '400', allocated: 24000, committed: 18000, paid: 15000 },
    ],
    matVsLab: {
      M: { allocated: 549000, committed: 498000 },
      L: { allocated: 205000, committed: 217000 },
      E: { allocated: 0, committed: 0 },
    },
    alerts: [
      { severity: 'critical', cost_code: 'L-203-001', title: 'L-203-001 เกินงบ 8.3%', allocated: 60000, committed: 65000, variance_pct: 8.3 },
      { severity: 'critical', cost_code: 'L-302-001', title: 'L-302-001 เกินงบ 4.8%', allocated: 145000, committed: 152000, variance_pct: 4.8 },
      { severity: 'warning', cost_code: 'M-301-002', title: 'M-301-002 ใกล้เต็มงบ (เหลือ 7.1%)', allocated: 84000, committed: 78000, variance_pct: -7.1 },
    ],
  };
}

function generateMockVariance() {
  return [
    { cost_code: 'M-205-003', notes: 'เหล็ก DB12', boq_rate: 285, best_quote: 268, worst_quote: 295, avg_quote: 280, quote_count: 3, best_variance_pct: -6.0, saving_potential: 17000 },
    { cost_code: 'M-203-001', notes: 'คอนกรีตฐานราก', boq_rate: 6500, best_quote: 6800, worst_quote: 7100, avg_quote: 6950, quote_count: 3, best_variance_pct: 4.6, saving_potential: -7200 },
    { cost_code: 'M-401-005', notes: 'ท่อ PVC 16mm', boq_rate: 120, best_quote: 115, worst_quote: 130, avg_quote: 122, quote_count: 2, best_variance_pct: -4.2, saving_potential: 1000 },
  ];
}

function generateMockBudgets() {
  const sp = generateMockSpend();
  return sp.rows.map((r, i) => ({
    id: i+1, cost_code: r.cost_code, allocated_qty: r.allocated_qty, unit: 'ลบ.เมตร',
    allocated_rate: r.allocated_rate, allocated_amount: r.allocated, notes: r.description,
  }));
}

window.CostAnalysisPage = CostAnalysisPage;
