// ============================================================================
// Analytics Dashboard — month-scoped business analytics for EMPHASIS.
//
// Adapted from the design file (Analytics Dashboard.html) and wired to real
// Supabase data. Our system has no "revenue" concept, so money KPIs are
// replaced by case-throughput metrics (closed cases, close rate, follow-ups
// pending). Sections:
//
//   1. KPI row (4)        — total / closed / close-rate / pending, each with
//                           month-over-month delta + daily sparkline.
//   2. Daily time series  — stacked-area, one band per appt type.
//   3. Pipeline funnel    — EM journey from ABO prospects' fu_state
//                           (cumulative business overview, not month-scoped).
//   4. Program table      — per-type cases / close-rate / avg duration.
//   5. Owner breakdown    — ตัวเอง / คู่ร่วมธุรกิจ / DL split.
//   6. Insights           — auto-generated observations.
//   7. Activity heatmap    — day-of-month intensity grid.
//
// Program-table rows open the TypeDetailModal drill-down (reused from
// calendar.jsx); appt clicks inside it route to 'calendar'.
// ============================================================================

// Bangkok-local YYYY-M-D key for an ISO timestamp.
function _bkkDayKey(iso) {
  const d = new Date(new Date(iso).getTime() + 7 * 3600 * 1000);
  return `${d.getUTCFullYear()}-${d.getUTCMonth()}-${d.getUTCDate()}`;
}
// Bangkok day-of-month (1-31) for an ISO timestamp.
function _bkkDayOfMonth(iso) {
  const d = new Date(new Date(iso).getTime() + 7 * 3600 * 1000);
  return d.getUTCDate();
}

// Small up/down/flat delta pill, comparing current vs previous value.
function DeltaPill({ cur, prev, unit = '%', invert = false, suffix = '' }) {
  if (prev === null || prev === undefined) {
    return <span className="text-[11px] text-ink-400 font-medium">ใหม่</span>;
  }
  const diff = cur - prev;
  const pct = prev !== 0 ? Math.round((diff / prev) * 100) : (cur > 0 ? 100 : 0);
  const isUp = diff > 0;
  const isFlat = diff === 0;
  // "good" direction can be inverted (e.g. fewer pending = good).
  const good = invert ? diff < 0 : diff > 0;
  const cls = isFlat ? 'flat' : good ? 'up' : 'down';
  const colorMap = {
    up:   { bg: '#E3F6EE', fg: '#047857' },
    down: { bg: '#FEE2E2', fg: '#B91C1C' },
    flat: { bg: '#F1F5F9', fg: '#64748B' },
  };
  const c = colorMap[cls];
  const arrow = isFlat ? '—' : isUp ? '▲' : '▼';
  const shown = unit === 'pp'
    ? `${Math.abs(diff).toFixed(1)}pp`
    : suffix
      ? `${Math.abs(diff)}${suffix}`
      : `${Math.abs(pct)}%`;
  return (
    <span className="inline-flex items-center gap-1 text-[11px] font-bold px-1.5 py-0.5 rounded-full num"
          style={{ background: c.bg, color: c.fg }}>
      {arrow} {shown}
    </span>
  );
}

// Tiny inline SVG sparkline from an array of numbers.
function Sparkline({ values, color, fill = true }) {
  if (!values || values.length === 0) return null;
  const W = 200, H = 30;
  const max = Math.max(1, ...values);
  const step = values.length > 1 ? W / (values.length - 1) : W;
  const pts = values.map((v, i) => [i * step, H - (v / max) * (H - 4) - 2]);
  const line = pts.map((p, i) => `${i === 0 ? 'M' : 'L'}${p[0].toFixed(1)},${p[1].toFixed(1)}`).join(' ');
  const area = `${line} L${W},${H} L0,${H} Z`;
  return (
    <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" className="w-full block" style={{ height: 30 }}>
      {fill && <path d={area} fill={color} fillOpacity="0.1" />}
      <path d={line} fill="none" stroke={color} strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round" />
    </svg>
  );
}

// One KPI card.
function KpiCard({ label, value, unit, color, tint, icon, deltaProps, spark, sub }) {
  return (
    <Card pad={false}>
      <div className="p-4 flex flex-col gap-2.5">
        <div className="flex items-center justify-between">
          <div className="text-[12.5px] text-ink-500 font-medium">{label}</div>
          <div className="w-8 h-8 rounded-lg flex items-center justify-center"
               style={{ background: tint, color }}>
            <Icon name={icon} className="w-4 h-4" />
          </div>
        </div>
        <div className="font-bold text-ink-900 leading-none" style={{ fontSize: 30, letterSpacing: '-0.8px' }}>
          {value}{unit && <span className="text-sm font-semibold text-ink-400 ml-1">{unit}</span>}
        </div>
        <div className="flex items-center gap-2 flex-wrap">
          {deltaProps && <DeltaPill {...deltaProps} />}
          {sub && <span className="text-[11px] text-ink-400">{sub}</span>}
        </div>
        {spark && <Sparkline values={spark.values} color={spark.color} fill={spark.fill} />}
      </div>
    </Card>
  );
}

// Stacked-area daily time series. `series` = [{ id, label, color, daily:[] }],
// `dayLabels` = array of x-axis labels (1-based day of month).
function StackedAreaChart({ series, days }) {
  const W = 720, H = 240, PADL = 32, PADR = 12, PADT = 12, PADB = 24;
  const [hover, setHover] = useState(null);

  const stacked = [];
  for (let i = 0; i < days; i++) {
    let total = 0;
    const row = {};
    for (const s of series) { row[s.id] = s.daily[i] || 0; total += row[s.id]; }
    row.total = total;
    stacked.push(row);
  }
  const maxY = Math.max(1, ...stacked.map(d => d.total));
  const yTicks = 4;
  const xStep = days > 1 ? (W - PADL - PADR) / (days - 1) : 0;
  const x = i => PADL + i * xStep;
  const y = v => H - PADB - (v / maxY) * (H - PADT - PADB);

  // Build stacked bands bottom-up.
  const bands = [];
  const running = stacked.map(() => 0);
  for (const s of series) {
    let top = '', bot = '';
    for (let i = 0; i < days; i++) {
      const nt = running[i] + (s.daily[i] || 0);
      top += `${i === 0 ? 'M' : 'L'}${x(i).toFixed(1)},${y(nt).toFixed(1)} `;
    }
    for (let i = days - 1; i >= 0; i--) bot += `L${x(i).toFixed(1)},${y(running[i]).toFixed(1)} `;
    let line = '';
    for (let i = 0; i < days; i++) {
      const nt = running[i] + (s.daily[i] || 0);
      line += `${i === 0 ? 'M' : 'L'}${x(i).toFixed(1)},${y(nt).toFixed(1)} `;
    }
    bands.push({ id: s.id, color: s.color, area: `${top}${bot}Z`, line });
    for (let i = 0; i < days; i++) running[i] += (s.daily[i] || 0);
  }

  const gridLines = [];
  for (let t = 0; t <= yTicks; t++) {
    const yy = PADT + t * ((H - PADT - PADB) / yTicks);
    const val = Math.round(maxY - (maxY / yTicks) * t);
    gridLines.push({ yy, val });
  }
  const xLabels = [];
  for (let i = 0; i < days; i += Math.max(1, Math.ceil(days / 8))) {
    xLabels.push({ i, label: i + 1 });
  }

  return (
    <div className="relative" style={{ height: 260 }}>
      <svg viewBox={`0 0 ${W} ${H}`} preserveAspectRatio="none" className="w-full h-full" style={{ overflow: 'visible' }}>
        {gridLines.map((g, idx) => (
          <g key={idx}>
            <line x1={PADL} y1={g.yy} x2={W - PADR} y2={g.yy} stroke="#F1F5F9" strokeWidth="1" />
            <text x={PADL - 6} y={g.yy + 3} textAnchor="end" style={{ fontSize: 10.5, fill: '#94A3B8' }}>{g.val}</text>
          </g>
        ))}
        {xLabels.map((xl, idx) => (
          <text key={idx} x={x(xl.i)} y={H - 6} textAnchor="middle" style={{ fontSize: 10.5, fill: '#94A3B8' }}>{xl.label}</text>
        ))}
        {bands.map(b => (
          <g key={b.id}>
            <path d={b.area} fill={b.color} fillOpacity="0.18" />
            <path d={b.line} fill="none" stroke={b.color} strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" />
          </g>
        ))}
        {hover !== null && (
          <line x1={x(hover)} y1={PADT} x2={x(hover)} y2={H - PADB} stroke="#0F172A" strokeOpacity="0.4" strokeDasharray="3 3" />
        )}
        {stacked.map((_, i) => (
          <rect key={i} x={x(i) - xStep / 2} y={PADT} width={xStep || W} height={H - PADT - PADB}
                fill="transparent"
                onMouseEnter={() => setHover(i)} onMouseLeave={() => setHover(null)} />
        ))}
      </svg>
      {hover !== null && stacked[hover] && (
        <div className="absolute pointer-events-none bg-ink-900 text-white text-[11.5px] rounded-lg px-2.5 py-2 shadow-lift"
             style={{
               left: `${(x(hover) / W) * 100}%`,
               top: 8,
               transform: 'translateX(-50%)',
               whiteSpace: 'nowrap',
               zIndex: 10,
             }}>
          <div className="text-[10px] text-ink-300 mb-1">วันที่ {hover + 1}</div>
          {series.map(s => (
            <div key={s.id} className="flex items-center gap-2">
              <span className="w-2 h-2 rounded-sm" style={{ background: s.color }} />
              {s.label} <b>{stacked[hover][s.id]}</b>
            </div>
          ))}
          <div className="mt-1 pt-1 border-t border-ink-600">รวม <b>{stacked[hover].total}</b></div>
        </div>
      )}
    </div>
  );
}

function CaseSummaryView({ currentUser, onNavigate }) {
  const { apptTypes } = useConfig();
  const [monthAppts, setMonthAppts] = useState([]);
  const [prevAppts, setPrevAppts] = useState([]);
  const [aboProspects, setAboProspects] = useState([]);
  const [typeDetailFor, setTypeDetailFor] = useState(null);
  const [loading, setLoading] = useState(true);
  const [monthRef, setMonthRef] = useState(() => {
    const d = new Date();
    return new Date(d.getFullYear(), d.getMonth(), 1);
  });

  // Fetch this-month + previous-month appts (for deltas) and the ABO
  // prospect list (for the cumulative pipeline funnel).
  useEffect(() => {
    let cancelled = false;
    (async () => {
      setLoading(true);
      const y = monthRef.getFullYear(), m = monthRef.getMonth();
      const curStart  = new Date(y, m, 1);
      const curEnd    = new Date(y, m + 1, 1);
      const prevStart = new Date(y, m - 1, 1);

      if (window.SB_READY && window.db?.appointments?.list) {
        const tasks = [
          window.db.appointments.list({ from: curStart.toISOString(),  to: curEnd.toISOString(),   weekStart: curStart }),
          window.db.appointments.list({ from: prevStart.toISOString(), to: curStart.toISOString(), weekStart: prevStart }),
        ];
        if (currentUser?.id && window.db?.abo?.list) {
          tasks.push(window.db.abo.list(currentUser.id));
        }
        const [curRes, prevRes, aboRes] = await Promise.all(tasks);
        if (cancelled) return;
        if (!curRes.error && curRes.data)   setMonthAppts(curRes.data);
        if (!prevRes.error && prevRes.data) setPrevAppts(prevRes.data);
        if (aboRes && !aboRes.error && aboRes.data) setAboProspects(aboRes.data);
        setLoading(false);
        return;
      }
      if (!cancelled) {
        setMonthAppts(window.APPOINTMENTS || []);
        setPrevAppts([]);
        setLoading(false);
      }
    })();
    return () => { cancelled = true; };
  }, [monthRef, currentUser?.id]);

  // Drill-down modal reused from calendar.jsx (program-table row click).
  const TypeDetailModal = window.TypeDetailModal;

  const openApptInCalendar = (appt) => {
    if (!appt) return;
    window.__emphasisPendingApptId = appt.id;
    onNavigate?.('calendar');
  };

  const monthLabel = monthRef.toLocaleDateString('th-TH', { month: 'long', year: 'numeric' });
  const goPrevMonth = () => setMonthRef(d => new Date(d.getFullYear(), d.getMonth() - 1, 1));
  const goNextMonth = () => setMonthRef(d => new Date(d.getFullYear(), d.getMonth() + 1, 1));
  const goThisMonth = () => { const d = new Date(); setMonthRef(new Date(d.getFullYear(), d.getMonth(), 1)); };

  // Types that count toward analytics (drop MT — นัดประชุม has no funnel role).
  const trackedTypes = apptTypes.filter(t => !t.noTrack);
  const daysInMonth = new Date(monthRef.getFullYear(), monthRef.getMonth() + 1, 0).getDate();
  // "Case programs" = tracked types minus Follow Up. FU is a follow-up
  // activity, not a new case, so it's excluded from case counts + the
  // program table (per user request). MT is already gone via noTrack.
  const caseTypes = trackedTypes.filter(t => t.id !== 'FU');

  // ─── Derived analytics ──────────────────────────────────────────────────
  // The dashboard focuses on PERSONAL cases (ตัวเอง + คู่ร่วมธุรกิจ). DL
  // cases are split out into their own bucket so they don't dilute the
  // personal close-rate / throughput numbers.
  const stats = useMemo(() => {
    const me = currentUser?.id;
    const isDl = a => !!a.dlProspectId || (!!a.dlId && !!me && a.dlId !== me);
    const isClosed = a => a.status === 'completed';
    const isPending = a => a.status === 'pending' || a.status === 'followup';
    const isFU = a => a.type === 'FU';

    // A "case" counts ONLY the real program types (BM / BI / 6W) — FU is a
    // follow-up, and MT/EV/CS/CL are non-tracked activities, so none of them
    // count toward case totals for anyone (personal OR DL).
    const caseTypeIds = new Set(caseTypes.map(t => t.id));
    const isCase = a => caseTypeIds.has(a.type);

    // Buckets.
    const personal     = monthAppts.filter(a => !isDl(a));            // self + partner
    const cases        = personal.filter(isCase);                     // BM/BI/6W only
    const prevPersonal = prevAppts.filter(a => !isDl(a));
    const prevCases    = prevPersonal.filter(isCase);
    const dlAppts      = monthAppts.filter(isDl);
    const dlCases      = dlAppts.filter(isCase);                      // BM/BI/6W only

    // KPI metrics — personal cases (BM/BI/6W).
    const total      = cases.length;
    const closed     = cases.filter(isClosed).length;
    const closeRate  = total ? (closed / total) * 100 : 0;
    // "ต้องตามต่อ" = personal pending/followup workload across all types
    // (FU follow-ups belong here — that IS the chase list).
    const pending    = personal.filter(isPending).length;

    const prevTotal     = prevCases.length;
    const prevClosed    = prevCases.filter(isClosed).length;
    const prevCloseRate = prevTotal ? (prevClosed / prevTotal) * 100 : 0;
    const prevPending   = prevPersonal.filter(isPending).length;

    // DL summary (separated out) — case types only.
    const dlCount  = dlCases.length;
    const dlClosed = dlCases.filter(isClosed).length;

    // Daily series — personal cases only (non-FU), per case-type. Drives the
    // stacked-area chart, sparklines, and heatmap so every visual agrees
    // with the KPI total.
    const dailyByType = {};
    for (const t of caseTypes) dailyByType[t.id] = new Array(daysInMonth).fill(0);
    const dailyTotal = new Array(daysInMonth).fill(0);
    const dailyClosed = new Array(daysInMonth).fill(0);
    for (const a of cases) {
      if (!a.scheduled_at) continue;
      const dom = _bkkDayOfMonth(a.scheduled_at);
      if (dom < 1 || dom > daysInMonth) continue;
      if (dailyByType[a.type]) dailyByType[a.type][dom - 1] += 1;
      dailyTotal[dom - 1] += 1;
      if (isClosed(a)) dailyClosed[dom - 1] += 1;
    }

    // Per-program rows — personal cases, split into เชล (self) vs แนน (partner).
    const perType = caseTypes.map(t => {
      const list  = cases.filter(a => a.type === t.id);
      const shell = list.filter(a => !a.isPartner);
      const nan   = list.filter(a => a.isPartner);
      const c = list.filter(isClosed).length;
      const durSum = list.reduce((s, a) => s + (a.dur || 1) * 60, 0);
      return {
        ...t,
        count: list.length,
        shell: shell.length,
        nan: nan.length,
        closed: c,
        closeRate: list.length ? (c / list.length) * 100 : 0,
        avgDur: list.length ? Math.round(durSum / list.length) : 0,
        prevCount: prevCases.filter(a => a.type === t.id).length,
      };
    }).sort((a, b) => b.count - a.count);

    // Personal owner split (เชล vs แนน), over cases.
    const owners = {
      self:    { label: currentUser?.name || 'ตัวเอง', color: '#2563EB', count: 0, closed: 0, byType: {} },
      partner: { label: currentUser?.partner_name || 'คู่ร่วมธุรกิจ', color: '#10B981', count: 0, closed: 0, byType: {} },
    };
    for (const a of cases) {
      const bucket = a.isPartner ? 'partner' : 'self';
      owners[bucket].count += 1;
      if (isClosed(a)) owners[bucket].closed += 1;
      owners[bucket].byType[a.type] = (owners[bucket].byType[a.type] || 0) + 1;
    }

    // Per-DL breakdown — group the separated DL cases by which DL they belong
    // to, so we can show "DL แต่ละคนทำกี่เคส" (split by BM/BI/6W) instead of
    // just one total.
    const dlMap = new Map();
    for (const a of dlCases) {
      const key = a.dlProspectId || a.dlId || a.dlName || 'unknown';
      const name = a.dlName || 'DL';
      if (!dlMap.has(key)) dlMap.set(key, { name, count: 0, closed: 0, byType: {} });
      const e = dlMap.get(key);
      e.count += 1;
      if (isClosed(a)) e.closed += 1;
      e.byType[a.type] = (e.byType[a.type] || 0) + 1;
    }
    const dlBreakdown = [...dlMap.values()].sort((a, b) => b.count - a.count);

    // Same-period comparison for the verdict hero: cap BOTH this month and
    // last month at the current elapsed day-of-month, so we compare "this
    // month so far" against "the same days last month". Personal cases only —
    // DL already excluded above. When viewing a completed past month we use
    // the whole month (cutoff = last day).
    const _now = new Date();
    const _isCurMonth = monthRef.getFullYear() === _now.getFullYear() && monthRef.getMonth() === _now.getMonth();
    const cutoffDay = _isCurMonth ? new Date(_now.getTime() + 7 * 3600 * 1000).getUTCDate() : daysInMonth;
    const _upto = arr => arr.filter(a => a.scheduled_at && _bkkDayOfMonth(a.scheduled_at) <= cutoffDay);
    const mtdCur = _upto(cases), mtdPrev = _upto(prevCases);
    const mtdTotal = mtdCur.length;
    const mtdClosed = mtdCur.filter(isClosed).length;
    const mtdPrevTotal = mtdPrev.length;
    const mtdPrevClosed = mtdPrev.filter(isClosed).length;
    const mtdCloseRate = mtdTotal ? (mtdClosed / mtdTotal) * 100 : 0;

    return {
      total, closed, pending, closeRate,
      prevTotal, prevClosed, prevPending, prevCloseRate,
      dlCount, dlClosed, dlBreakdown,
      dailyByType, dailyTotal, dailyClosed,
      perType, owners,
      cutoffDay, isCurMonth: _isCurMonth,
      mtdTotal, mtdClosed, mtdPrevTotal, mtdPrevClosed, mtdCloseRate,
    };
  }, [monthAppts, prevAppts, caseTypes, daysInMonth, currentUser, monthRef]);

  // ─── Pipeline funnel from ABO prospects' fu_state (cumulative) ──────────
  const funnel = useMemo(() => {
    const rows = aboProspects || [];
    const fs = p => p.fu_state || {};
    const all = rows.length;
    const bm = rows.filter(p => fs(p).bmodel).length;
    const bi = rows.filter(p => fs(p).bmodel && fs(p).bincome).length;
    const emBegin = rows.filter(p => {
      const s = fs(p);
      return s.bt3 || ['mtg1', 'mtg2', 'mtg3', 'mtg4'].every(k => s[k]);
    }).length;
    const mfinity = rows.filter(p => fs(p).bt3).length;
    const pct = n => all ? Math.round((n / all) * 100) : 0;
    return [
      { label: 'Prospect ทั้งหมด', n: all,     pct: 100,         color: '#1E40AF' },
      { label: 'ผ่าน BM',         n: bm,      pct: pct(bm),     color: '#2563EB' },
      { label: 'ผ่าน BI',         n: bi,      pct: pct(bi),     color: '#3B82F6' },
      { label: 'จบ EM Begin',     n: emBegin, pct: pct(emBegin),color: '#60A5FA' },
      { label: 'MFinity',         n: mfinity, pct: pct(mfinity),color: '#10B981' },
    ];
  }, [aboProspects]);

  // ─── Auto insights ──────────────────────────────────────────────────────
  const insights = useMemo(() => {
    const out = [];
    // Type with cases but 0 closes.
    const stuck = stats.perType.find(t => t.count >= 2 && t.closed === 0);
    if (stuck) {
      out.push({
        kind: 'warn',
        title: `${stuck.full || stuck.label} ปิดได้ 0 เคสในเดือนนี้`,
        desc: `มี ${stuck.count} เคส แต่ยังไม่มีปิดสำเร็จ — ลองดูว่าติดที่ขั้นไหน`,
      });
    }
    // Biggest grower vs prev month.
    const grower = [...stats.perType].filter(t => t.prevCount > 0)
      .sort((a, b) => (b.count - b.prevCount) - (a.count - a.prevCount))[0];
    if (grower && grower.count > grower.prevCount) {
      const up = grower.count - grower.prevCount;
      out.push({
        kind: 'good',
        title: `${grower.full || grower.label} โตขึ้น +${up} เคสจากเดือนก่อน`,
        desc: `เดือนนี้ ${grower.count} เคส (เดือนก่อน ${grower.prevCount}) — เป็นช่องทางที่กำลังมา`,
      });
    }
    // Pending / overdue.
    if (stats.pending > 0) {
      const now = Date.now();
      const overdue = monthAppts.filter(a =>
        (a.status === 'pending' || a.status === 'followup') &&
        a.scheduled_at && new Date(a.scheduled_at).getTime() < now
      ).length;
      out.push({
        kind: 'info',
        title: `มีเคสค้างตามต่อ ${stats.pending} เคส`,
        desc: overdue > 0
          ? `${overdue} เคสเลยกำหนดแล้ว — แนะนำติดต่อกลับวันนี้ก่อนหลุดมือ`
          : `ยังไม่มีเคสเลยกำหนด — ตามต่อให้ทันตามนัด`,
        action: 'เปิด EM Calendar →',
      });
    }
    return out;
  }, [stats, monthAppts]);

  // Short display names for the owner columns (first word keeps headers tight).
  const shellName = (currentUser?.name || 'ตัวเอง').split(' ')[0];
  const nanName   = (currentUser?.partner_name || 'คู่ร่วมธุรกิจ').split(' ')[0];

  // Per-person split by case type (BM / BI / 6W …) — small coloured chips,
  // zeros dimmed. Reused for both the main team and each DL.
  const renderTypeBreakdown = (byType) => (
    <div className="flex items-center gap-1 flex-wrap mt-1">
      {caseTypes.map(t => {
        const n = byType?.[t.id] || 0;
        return (
          <span key={t.id}
            className="inline-flex items-center gap-0.5 px-1.5 py-0.5 rounded text-[10px] font-semibold num"
            style={n ? { background: t.color + '1A', color: t.color } : { background: '#F1F5F9', color: '#CBD5E1' }}
            title={`${t.full || t.label}: ${n} เคส`}>
            {t.label} {n}
          </span>
        );
      })}
    </div>
  );

  // One person tile — name + big case count + closed pill + BM/BI/6W chips.
  // Plain render fn (not a component) so it never remounts on re-render.
  const renderPerson = ({ key, badge, color, name, count, closed, byType }) => (
    <div key={key} className="rounded-xl border border-ink-100 bg-white p-3 hover:shadow-card transition-shadow">
      <div className="flex items-center gap-2 mb-2">
        <span className="w-7 h-7 rounded-lg flex items-center justify-center text-white text-[12px] font-bold flex-shrink-0" style={{ background: color }}>{badge}</span>
        <span className="text-[13px] font-semibold text-ink-900 truncate flex-1 min-w-0">{name}</span>
      </div>
      <div className="flex items-baseline gap-1">
        <span className="text-2xl font-bold text-ink-900 num leading-none">{count}</span>
        <span className="text-[11px] text-ink-400">เคส</span>
        <span className="ml-auto text-[11px] font-semibold px-1.5 py-0.5 rounded-full bg-emerald-50 text-emerald-600">ปิด {closed}</span>
      </div>
      {renderTypeBreakdown(byType)}
    </div>
  );

  // Time-series payload — case programs only (no FU).
  const series = caseTypes.map(t => ({
    id: t.id, label: t.label, color: t.color, daily: stats.dailyByType[t.id] || [],
  }));

  const insightStyle = {
    warn: { bg: '#FEF3C7', border: '#FDE68A', icoBg: '#FDE68A', icoFg: '#B45309', icon: 'flag' },
    good: { bg: '#E3F6EE', border: '#A7F3D0', icoBg: '#A7F3D0', icoFg: '#047857', icon: 'check' },
    info: { bg: '#EAF1FE', border: '#C7DBFD', icoBg: '#C7DBFD', icoFg: '#1D4ED8', icon: 'info' },
  };

  // ── v2 dashboard derived values ──
  // Verdict hero compares PERSONAL cases (no DL) over the SAME period — this
  // month so far vs the same days last month (stats.mtd*).
  const growing       = stats.mtdTotal >= stats.mtdPrevTotal;
  const totalDeltaPct = stats.mtdPrevTotal ? Math.round(((stats.mtdTotal - stats.mtdPrevTotal) / stats.mtdPrevTotal) * 100) : 0;
  // Weakest program = lowest close-rate among programs that have cases.
  const weakest = [...stats.perType].filter(t => t.count > 0).sort((a, b) => a.closeRate - b.closeRate)[0] || null;
  const overdue = monthAppts.filter(a => (a.status === 'pending' || a.status === 'followup') && a.scheduled_at && new Date(a.scheduled_at).getTime() < Date.now()).length;
  // ทีมเด่นเดือนนี้ — DL only (ไม่รวม upline เชล/แนน), ranked by cases,
  // each split by BM/BI/6W.
  const leaderboard = stats.dlBreakdown
    .map(d => ({ name: d.name, badge: '💎', color: '#F59E0B', count: d.count, closed: d.closed, byType: d.byType, isDl: true }))
    .filter(p => p.count > 0)
    .sort((a, b) => (b.count - a.count) || (b.closed - a.closed));

  return (
    <div className="flex flex-col gap-4">
      <SectionHead
        eyebrow="Analytics · Performance"
        title="วิเคราะห์ภาพรวมธุรกิจ"
        right={
          <div className="flex items-center gap-2">
            <button onClick={goPrevMonth}
              className="w-8 h-8 rounded-lg border border-ink-200 flex items-center justify-center hover:bg-ink-50"
              title="เดือนก่อนหน้า">
              <Icon name="chevL" className="w-3.5 h-3.5" />
            </button>
            <div className="text-sm font-semibold text-ink-900 min-w-[120px] text-center">{monthLabel}</div>
            <button onClick={goNextMonth}
              className="w-8 h-8 rounded-lg border border-ink-200 flex items-center justify-center hover:bg-ink-50"
              title="เดือนถัดไป">
              <Icon name="chevR" className="w-3.5 h-3.5" />
            </button>
            <button onClick={goThisMonth}
              className="text-xs font-medium text-brand-600 hover:text-brand-700 ml-1">
              เดือนนี้
            </button>
          </div>
        }
      />

      <div className="text-[11px] text-ink-400 -mt-2">
        สรุปข้อมูลเดือน {monthLabel} · เทียบกับเดือนก่อนหน้า
        {loading && <span className="ml-1">· กำลังโหลด…</span>}
      </div>

      {/* ── Verdict hero ── */}
      <div className="rounded-2xl overflow-hidden text-white grid grid-cols-1 lg:grid-cols-[1.55fr_1fr]"
           style={{ background: 'linear-gradient(118deg,#16367F 0%,#1D4ED8 58%,#2563EB 100%)', boxShadow: '0 18px 40px -22px rgba(29,78,216,0.7)' }}>
        <div className="p-6 sm:p-7">
          <span className="inline-flex items-center gap-2 bg-white/15 text-white text-[12px] font-semibold px-3 py-1.5 rounded-full">
            <span className="w-2 h-2 rounded-full" style={{ background: '#4ADE80', boxShadow: '0 0 0 4px rgba(74,222,128,0.25)' }} />
            {growing ? 'เดือนนี้ไปได้ดี' : 'เดือนนี้ต้องเร่งมือ'}
          </span>
          <h2 className="mt-4 text-[21px] sm:text-[24px] font-bold leading-snug">
            จำนวนเคส{' '}
            <span style={{ background: growing ? 'linear-gradient(transparent 62%, rgba(74,222,128,0.45) 62%)' : 'linear-gradient(transparent 62%, rgba(251,191,36,0.5) 62%)', padding: '0 2px' }}>
              {growing ? 'เติบโตขึ้นจากเดือนก่อน' : 'ลดลงจากเดือนก่อน'}
            </span>
            {weakest && (
              <> · จุดที่ต้องดูคือ{' '}
                <span style={{ background: 'linear-gradient(transparent 62%, rgba(251,191,36,0.5) 62%)', padding: '0 2px' }}>
                  {weakest.full || weakest.label} ที่ยังปิดได้น้อย ({weakest.closeRate.toFixed(0)}%)
                </span>
              </>
            )}
          </h2>
          <div className="flex gap-6 sm:gap-9 mt-5 flex-wrap">
            <div>
              <div className="text-white/70 text-[11.5px] mb-1">เคสปิดได้</div>
              <div className="text-[19px] font-bold num flex items-baseline gap-1.5">
                {stats.mtdClosed}
                <span className="text-[12px] font-semibold text-emerald-200">{stats.mtdPrevClosed ? `${stats.mtdClosed >= stats.mtdPrevClosed ? '▲' : '▼'} ${Math.abs(stats.mtdClosed - stats.mtdPrevClosed)}` : 'ใหม่'}</span>
              </div>
            </div>
            <div>
              <div className="text-white/70 text-[11.5px] mb-1">อัตราปิด</div>
              <div className="text-[19px] font-bold num">{stats.mtdCloseRate.toFixed(1)}<span className="text-[12px] font-semibold text-white/70 ml-0.5">%</span></div>
            </div>
            <div>
              <div className="text-white/70 text-[11.5px] mb-1">เคสต้องตามต่อ</div>
              <div className="text-[19px] font-bold num flex items-baseline gap-1.5">
                {stats.pending}
                {overdue > 0 && <span className="text-[12px] font-semibold text-rose-200">{overdue} เลยกำหนด</span>}
              </div>
            </div>
          </div>
        </div>
        <div className="p-6 sm:p-7 border-t lg:border-t-0 lg:border-l border-white/15 bg-white/5 flex flex-col justify-center">
          <div className="text-white/70 text-[12px]">เคสส่วนตัวเดือนนี้{stats.isCurMonth ? ` (1–${stats.cutoffDay})` : ''}</div>
          <div className="font-extrabold leading-none mt-2 num" style={{ fontSize: 44, letterSpacing: '-1.5px' }}>
            {stats.mtdTotal}<span className="text-[18px] font-bold text-white/80 ml-1">เคส</span>
          </div>
          <div className="inline-flex items-center gap-1.5 mt-3 w-fit px-2.5 py-1 rounded-full text-[12.5px] font-bold num"
               style={{ background: growing ? 'rgba(74,222,128,0.2)' : 'rgba(251,191,36,0.2)', color: growing ? '#BBF7D0' : '#FDE68A' }}>
            {growing ? '▲' : '▼'} {Math.abs(totalDeltaPct)}% เทียบช่วงเดียวกันเดือนก่อน ({stats.mtdPrevTotal})
          </div>
          {stats.total > 0 && <div className="mt-3"><Sparkline values={stats.dailyTotal} color="#A7F3D0" fill /></div>}
        </div>
      </div>

      {/* ── KPI strip ── */}
      <div className="grid grid-cols-2 lg:grid-cols-4 gap-3">
        <KpiCard label="เคสรวมเดือนนี้" value={stats.total} unit="เคส" color="#2563EB" tint="#E8EFFE" icon="list"
          deltaProps={{ cur: stats.total, prev: loading ? null : stats.prevTotal }} sub={`ก่อน ${stats.prevTotal}`}
          spark={{ values: stats.dailyTotal, color: '#2563EB', fill: true }} />
        <KpiCard label="ปิดสำเร็จ" value={stats.closed} unit="เคส" color="#10B981" tint="#E3F6EE" icon="check"
          deltaProps={{ cur: stats.closed, prev: loading ? null : stats.prevClosed }} sub={`ก่อน ${stats.prevClosed}`}
          spark={{ values: stats.dailyClosed, color: '#10B981', fill: true }} />
        <KpiCard label="อัตราปิด (Close rate)" value={stats.closeRate.toFixed(1)} unit="%" color="#F59E0B" tint="#FEF1DC" icon="target"
          deltaProps={{ cur: stats.closeRate, prev: loading ? null : stats.prevCloseRate, unit: 'pp' }} sub={`${stats.closed}/${stats.total} เคส`} />
        <KpiCard label="ต้องตามต่อ" value={stats.pending} unit="เคส" color="#06B6D4" tint="#E0F6FB" icon="clock"
          deltaProps={{ cur: stats.pending, prev: loading ? null : stats.prevPending, invert: true, suffix: '' }} sub="Pending + Follow-up" />
      </div>

      {/* ── Time series + Funnel ── */}
      <div className="grid grid-cols-1 lg:grid-cols-3 gap-4">
        <div className="lg:col-span-2">
          <Card title={
            <div>
              <div className="text-sm font-semibold text-ink-900">เคสรายวัน · {monthLabel}</div>
              <div className="text-[11px] text-ink-400 mt-0.5">เฉพาะเคสส่วนตัว · รวม {stats.total} เคส</div>
            </div>
          } action={
            <div className="flex items-center gap-3 flex-wrap text-[11px] text-ink-400">
              {caseTypes.map(t => (
                <span key={t.id} className="inline-flex items-center gap-1.5">
                  <span className="w-2 h-2 rounded-sm" style={{ background: t.color }} />{t.label}
                </span>
              ))}
            </div>
          }>
            {stats.total > 0 ? (
              <StackedAreaChart series={series} days={daysInMonth} />
            ) : (
              <div className="h-[260px] flex items-center justify-center text-sm text-ink-400">
                ยังไม่มีเคสในเดือนนี้
              </div>
            )}
          </Card>
        </div>

        {/* ทีมเด่นเดือนนี้ — leaderboard, each person split by BM/BI/6W. */}
        <Card title={
          <div>
            <div className="text-sm font-semibold text-ink-900">ทีมเด่นเดือนนี้</div>
            <div className="text-[11px] text-ink-400 mt-0.5">เรียงตามจำนวนเคส · แยก BM / BI / 6W ของ DL แต่ละคน</div>
          </div>
        }>
          {leaderboard.length === 0 ? (
            <div className="text-[12px] text-ink-400 text-center py-8">ยังไม่มีเคสในเดือนนี้</div>
          ) : (
            <div className="flex flex-col mt-1">
              {leaderboard.map((p, i) => {
                const rankColor = ['#B45309', '#64748B', '#92400E'][i] || '#94A3B8';
                return (
                  <div key={i} className="grid items-center gap-3 py-3 border-b border-ink-50 last:border-0"
                       style={{ gridTemplateColumns: '20px 36px 1fr auto' }}>
                    <div className="text-center font-bold text-[13px] num" style={{ color: rankColor }}>{i + 1}</div>
                    <div className="w-9 h-9 rounded-full text-white font-bold text-[13px] flex items-center justify-center flex-shrink-0" style={{ background: p.color }}>{p.badge}</div>
                    <div className="min-w-0">
                      <div className="text-[13.5px] font-semibold text-ink-900 truncate">
                        {p.name}
                        {p.isDl && <span className="text-[9px] font-bold text-amber-600 bg-amber-50 px-1 py-0.5 rounded ml-1.5 align-middle">DL</span>}
                      </div>
                      <div className="flex items-center gap-1 flex-wrap mt-1">
                        {caseTypes.map(t => {
                          const n = p.byType?.[t.id] || 0;
                          return (
                            <span key={t.id} className="px-1.5 py-0.5 rounded text-[10px] font-semibold num"
                                  style={n ? { background: t.color + '1A', color: t.color } : { background: '#F1F5F9', color: '#CBD5E1' }}>
                              {t.label} {n}
                            </span>
                          );
                        })}
                      </div>
                    </div>
                    <div className="text-right flex-shrink-0">
                      <div className="text-[18px] font-extrabold text-ink-900 num leading-none">
                        {p.count}<span className="text-[11px] font-semibold text-ink-400 ml-0.5">เคส</span>
                      </div>
                      <div className="text-[10.5px] text-emerald-600 font-semibold mt-1">ปิด {p.closed}</div>
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </Card>
      </div>

      {/* ── Program table (full width) ── */}
      <Card title={
            <div>
              <div className="text-sm font-semibold text-ink-900">ประสิทธิภาพแต่ละโปรแกรม</div>
              <div className="text-[11px] text-ink-400 mt-0.5">เฉพาะเคสส่วนตัว · แยก{shellName} / {nanName}</div>
            </div>
          } pad={false}>
            <div className="px-4 pb-3 pt-1 overflow-x-auto">
              <table className="w-full" style={{ borderCollapse: 'collapse', minWidth: 480 }}>
                <thead>
                  <tr className="text-[11px] uppercase tracking-wide text-ink-400">
                    <th className="text-left font-semibold pb-2.5 border-b border-ink-100">โปรแกรม</th>
                    <th className="text-right font-semibold pb-2.5 border-b border-ink-100" style={{ color: '#2563EB' }}>{shellName}</th>
                    <th className="text-right font-semibold pb-2.5 border-b border-ink-100" style={{ color: '#10B981' }}>{nanName}</th>
                    <th className="text-right font-semibold pb-2.5 border-b border-ink-100">รวม</th>
                    <th className="text-left font-semibold pb-2.5 border-b border-ink-100 pl-3">Close rate</th>
                    <th className="text-right font-semibold pb-2.5 border-b border-ink-100">ปิด</th>
                  </tr>
                </thead>
                <tbody>
                  {stats.perType.map(t => (
                    <tr key={t.id} className="cursor-pointer hover:bg-ink-50/50"
                        onClick={() => setTypeDetailFor(t.id)}>
                      <td className="py-2.5 border-b border-ink-50">
                        <div className="flex items-center gap-2.5">
                          <span className="w-8 h-8 rounded-lg text-white font-bold text-[11px] flex items-center justify-center flex-shrink-0"
                                style={{ background: t.color }}>{t.label}</span>
                          <div className="min-w-0">
                            <div className="text-[13.5px] font-semibold text-ink-900 truncate">{t.full || t.label}</div>
                            <div className="text-[11px] text-ink-400 truncate">{t.desc || '—'}</div>
                          </div>
                        </div>
                      </td>
                      <td className="py-2.5 border-b border-ink-50 text-right font-semibold num" style={{ color: t.shell ? '#2563EB' : '#CBD5E1' }}>{t.shell}</td>
                      <td className="py-2.5 border-b border-ink-50 text-right font-semibold num" style={{ color: t.nan ? '#10B981' : '#CBD5E1' }}>{t.nan}</td>
                      <td className="py-2.5 border-b border-ink-50 text-right font-bold text-ink-900 num">{t.count}</td>
                      <td className="py-2.5 border-b border-ink-50 pl-3" style={{ minWidth: 110 }}>
                        <div className="flex items-center justify-between text-[11px] text-ink-400 font-semibold mb-1">
                          <span>{t.closeRate.toFixed(0)}%</span>
                        </div>
                        <div className="h-1.5 w-full bg-ink-100 rounded-full overflow-hidden">
                          <div className="h-full rounded-full" style={{ width: `${t.closeRate}%`, background: t.color }} />
                        </div>
                      </td>
                      <td className="py-2.5 border-b border-ink-50 text-right font-semibold text-ink-900 num">{t.closed}</td>
                    </tr>
                  ))}
                  {/* Sum row */}
                  {stats.total > 0 && (
                    <tr className="font-bold">
                      <td className="py-2.5 text-[12px] text-ink-500 uppercase tracking-wide">รวมทั้งหมด</td>
                      <td className="py-2.5 text-right num" style={{ color: '#2563EB' }}>{stats.owners.self.count}</td>
                      <td className="py-2.5 text-right num" style={{ color: '#10B981' }}>{stats.owners.partner.count}</td>
                      <td className="py-2.5 text-right text-ink-900 num">{stats.total}</td>
                      <td className="py-2.5 pl-3 text-[12px] text-ink-400 num">{stats.closeRate.toFixed(0)}%</td>
                      <td className="py-2.5 text-right text-ink-900 num">{stats.closed}</td>
                    </tr>
                  )}
                </tbody>
              </table>
              {stats.total === 0 && (
                <div className="py-6 text-center text-sm text-ink-400">ยังไม่มีเคสส่วนตัวในเดือนนี้</div>
              )}
            </div>
          </Card>

      {/* ── Insights + Activity heatmap ── */}
      <div className="grid grid-cols-1 lg:grid-cols-5 gap-4">
        <div className="lg:col-span-2">
          <Card title={
            <div>
              <div className="text-sm font-semibold text-ink-900">ข้อสังเกตประจำเดือน</div>
              <div className="text-[11px] text-ink-400 mt-0.5">วิเคราะห์อัตโนมัติจากข้อมูล</div>
            </div>
          }>
            <div className="flex flex-col gap-2.5 mt-1">
              {insights.length === 0 && (
                <div className="text-sm text-ink-400 py-4 text-center">ยังไม่มีข้อสังเกต — ข้อมูลน้อยเกินไป</div>
              )}
              {insights.map((ins, i) => {
                const s = insightStyle[ins.kind];
                return (
                  <div key={i} className="flex gap-3 p-3 rounded-xl border items-start"
                       style={{ background: s.bg, borderColor: s.border }}>
                    <div className="w-7 h-7 rounded-lg flex items-center justify-center flex-shrink-0"
                         style={{ background: s.icoBg, color: s.icoFg }}>
                      <Icon name={s.icon} className="w-4 h-4" />
                    </div>
                    <div className="min-w-0">
                      <div className="text-[13px] font-bold text-ink-900 leading-snug">{ins.title}</div>
                      <div className="text-[12px] text-ink-600 mt-1 leading-relaxed">{ins.desc}</div>
                      {ins.action && (
                        <button onClick={() => onNavigate?.('calendar')}
                          className="text-[11.5px] font-semibold text-brand-600 mt-1.5 hover:underline">
                          {ins.action}
                        </button>
                      )}
                    </div>
                  </div>
                );
              })}
            </div>
          </Card>
        </div>

        {/* Activity heatmap */}
        <div className="lg:col-span-3">
          <Card title={
            <div>
              <div className="text-sm font-semibold text-ink-900">กิจกรรมรายวัน · {monthLabel}</div>
              <div className="text-[11px] text-ink-400 mt-0.5">ความหนาแน่นของนัดแต่ละวัน</div>
            </div>
          } action={
            <div className="flex items-center gap-1.5 text-[10px] text-ink-400">
              <span>น้อย</span>
              {['#F1F5F9', '#BFDBFE', '#60A5FA', '#2563EB', '#1E3A8A'].map(c => (
                <span key={c} className="w-2.5 h-2.5 rounded-sm" style={{ background: c }} />
              ))}
              <span>มาก</span>
            </div>
          }>
            <div className="grid gap-1 mt-1" style={{ gridTemplateColumns: 'repeat(7, 1fr)' }}>
              {stats.dailyTotal.map((v, i) => {
                const lvl = v >= 7 ? 4 : v >= 5 ? 3 : v >= 3 ? 2 : v >= 1 ? 1 : 0;
                const bg = ['#F1F5F9', '#BFDBFE', '#60A5FA', '#2563EB', '#1E3A8A'][lvl];
                return (
                  <div key={i} title={`วันที่ ${i + 1} · ${v} เคส`}
                       className="rounded-md flex items-center justify-center text-[10px] font-semibold num"
                       style={{ aspectRatio: '1', background: bg, color: lvl >= 3 ? '#fff' : '#94A3B8' }}>
                    {i + 1}
                  </div>
                );
              })}
            </div>
            {(() => {
              // Busiest day summary.
              let maxI = -1, maxV = 0;
              stats.dailyTotal.forEach((v, i) => { if (v > maxV) { maxV = v; maxI = i; } });
              const peakClosed = maxI >= 0 ? stats.dailyClosed[maxI] : 0;
              return (
                <div className="mt-3 pt-3 border-t border-ink-100 grid grid-cols-3 gap-2">
                  <div>
                    <div className="text-[11px] text-ink-400">วันที่หนาแน่นสุด</div>
                    <div className="text-sm font-bold text-ink-900 mt-0.5 num">{maxV > 0 ? `วันที่ ${maxI + 1}` : '—'}</div>
                    <div className="text-[11px] text-ink-400 mt-0.5 num">{maxV} เคส · ปิด {peakClosed}</div>
                  </div>
                  <div>
                    <div className="text-[11px] text-ink-400">เฉลี่ยต่อวัน</div>
                    <div className="text-sm font-bold text-ink-900 mt-0.5 num">
                      {(stats.total / daysInMonth).toFixed(1)}
                    </div>
                    <div className="text-[11px] text-ink-400 mt-0.5">เคส/วัน</div>
                  </div>
                  <div>
                    <div className="text-[11px] text-ink-400">อัตราปิดรวม</div>
                    <div className="text-sm font-bold mt-0.5 num" style={{ color: '#10B981' }}>
                      {stats.closeRate.toFixed(0)}%
                    </div>
                    <div className="text-[11px] text-ink-400 mt-0.5 num">ปิด {stats.closed}</div>
                  </div>
                </div>
              );
            })()}
          </Card>
        </div>
      </div>

      {/* Drill-down */}
      {TypeDetailModal && (
        <TypeDetailModal
          open={!!typeDetailFor}
          onClose={() => setTypeDetailFor(null)}
          typeId={typeDetailFor}
          appts={monthAppts.filter(a => a.type === typeDetailFor)}
          onOpenAppt={(a) => { setTypeDetailFor(null); openApptInCalendar(a); }}
        />
      )}
    </div>
  );
}

window.CaseSummaryView = CaseSummaryView;
