// Calendar — month view & day-detail (split from calendar.jsx).
// MonthGridView, MonthChip, DayDetailModal, ActivityLog, TypeStatTile,
// FollowUpCard, TypeDetailModal. Also owns the window.* exports for cases.jsx.

// ============== Month grid view ==============
// Apple-Calendar-style 6-row month overview. Each cell = one calendar day
// with the date number + colored chips for that day's appts. Tapping a
// chip opens EditApptModal; tapping an empty area opens the new-appt
// quick picker positioned on that date.
function MonthGridView({ monthAppts, monthRef, apptTypes, onOpenAppt, onPickCell, onPrevMonth, onNextMonth, onThisMonth }) {
  const TH_DOW = ['จ.', 'อ.', 'พ.', 'พฤ.', 'ศ.', 'ส.', 'อา.'];
  const TH_MO  = ['ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.','ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.'];

  // Build a 6×7 grid that fully covers the target month. Start the grid
  // on the Monday of the week containing the 1st, then run 42 cells.
  const grid = useMemo(() => {
    const y = monthRef.getFullYear();
    const m = monthRef.getMonth();
    const first = new Date(y, m, 1);
    // Mon=1..Sun=7 (treat Sun as 7 so the week starts on Monday)
    const dow = first.getDay() === 0 ? 7 : first.getDay();
    const start = new Date(y, m, 1 - (dow - 1));
    return Array.from({ length: 42 }, (_, i) => {
      const d = new Date(start.getFullYear(), start.getMonth(), start.getDate() + i);
      return d;
    });
  }, [monthRef]);

  // Bucket appts by YYYY-MM-DD in Bangkok TZ.
  const byDate = useMemo(() => {
    const map = new Map();
    for (const a of monthAppts) {
      if (!a.scheduled_at) continue;
      const bkk = new Date(new Date(a.scheduled_at).getTime() + 7 * 3600 * 1000);
      const key = `${bkk.getUTCFullYear()}-${bkk.getUTCMonth()}-${bkk.getUTCDate()}`;
      if (!map.has(key)) map.set(key, []);
      map.get(key).push(a);
    }
    // Sort each bucket by time ascending.
    for (const list of map.values()) {
      list.sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at));
    }
    return map;
  }, [monthAppts]);

  const todayKey = (() => {
    const bkk = new Date(Date.now() + 7 * 3600 * 1000);
    return `${bkk.getUTCFullYear()}-${bkk.getUTCMonth()}-${bkk.getUTCDate()}`;
  })();

  const targetMonth = monthRef.getMonth();
  const monthLabel = `${TH_MO[targetMonth]} ${monthRef.getFullYear() + 543}`;

  return (
    <Card pad={false}>
      {/* Header — month label + nav arrows. The hint moves below the
          row on mobile so the arrows stay reachable on small screens. */}
      <div className="px-3 sm:px-5 pt-4 pb-2 border-b border-ink-100">
        <div className="flex items-center justify-between gap-2">
          <div className="flex items-center gap-1 sm:gap-2">
            {onPrevMonth && (
              <button onClick={onPrevMonth}
                className="w-8 h-8 rounded-lg border border-ink-200 flex items-center justify-center hover:bg-ink-50 transition-colors"
                title="เดือนก่อนหน้า">
                <Icon name="chevL" className="w-3.5 h-3.5" />
              </button>
            )}
            <h3 className="text-sm sm:text-base font-semibold text-ink-900 min-w-[112px] sm:min-w-[140px] text-center">
              {monthLabel}
            </h3>
            {onNextMonth && (
              <button onClick={onNextMonth}
                className="w-8 h-8 rounded-lg border border-ink-200 flex items-center justify-center hover:bg-ink-50 transition-colors"
                title="เดือนถัดไป">
                <Icon name="chevR" className="w-3.5 h-3.5" />
              </button>
            )}
            {onThisMonth && (
              <button onClick={onThisMonth}
                className="text-xs font-medium text-brand-600 hover:text-brand-700 ml-1"
                title="กลับไปเดือนนี้">
                เดือนนี้
              </button>
            )}
            <span className="hidden sm:inline text-[11px] text-ink-400 num ml-2">· {monthAppts.length} เคส</span>
          </div>
          <div className="hidden sm:block text-[10px] text-ink-400">แตะวันที่ → สร้างนัด · แตะเคส → แก้</div>
        </div>
        <div className="sm:hidden mt-1 flex items-center justify-between text-[10px] text-ink-400">
          <span className="num">{monthAppts.length} เคส</span>
          <span>แตะวันที่ → ดูทั้งวัน</span>
        </div>
      </div>

      {/* Weekday row */}
      <div className="grid grid-cols-7 border-b border-ink-100 bg-ink-50/40">
        {TH_DOW.map(d => (
          <div key={d} className="text-center text-[10px] uppercase tracking-wider font-semibold text-ink-500 py-1.5">
            {d}
          </div>
        ))}
      </div>

      {/* 6 rows × 7 cols */}
      <div className="grid grid-cols-7" style={{ gridAutoRows: 'minmax(80px, auto)' }}>
        {grid.map((d, i) => {
          const inMonth = d.getMonth() === targetMonth;
          const key = `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
          const isToday = key === todayKey;
          const list = byDate.get(key) || [];
          const isWeekend = i % 7 >= 5;
          // Mobile shows up to 5 chips per cell so a typical busy day fits
          // without an overflow chip. Desktop keeps the compact 4-chip cap.
          const CHIPS_DESKTOP = 4;
          const CHIPS_MOBILE  = 5;
          return (
            <div key={i}
              className={`relative border-r border-b border-ink-100 p-1 sm:p-1.5 cursor-pointer transition-colors ${
                isToday ? 'bg-brand-50/60' : isWeekend && inMonth ? 'bg-ink-50/30' : !inMonth ? 'bg-ink-50/50' : 'bg-white'
              } hover:bg-brand-50/30`}
              onClick={(e) => {
                if (e.target !== e.currentTarget && e.target.closest('[data-appt-chip]')) return;
                onPickCell?.(d, 10);
              }}>
              <div className={`flex items-center justify-between mb-1 text-[10px] sm:text-[11px] ${
                isToday ? 'font-bold text-brand-700' : !inMonth ? 'text-ink-300' : isWeekend ? 'text-ink-400' : 'text-ink-700'
              }`}>
                <span className="num">{d.getDate()}</span>
                {isToday && (
                  <span className="text-[8px] sm:text-[9px] uppercase tracking-wider font-bold text-rose-500">วันนี้</span>
                )}
              </div>

              {/* Chip list — mobile and desktop have different caps AND
                  different tap targets. Mobile chips bubble up to the
                  cell-level "open this day" handler (so the user always
                  sees the full day list, since the chips on a phone are
                  too small to make a confident pick). Desktop chips
                  remain individually clickable — they open EditApptModal
                  directly because the chips are larger and the user
                  knows what they're tapping. */}
              <div className="space-y-0.5 sm:hidden">
                {list.slice(0, CHIPS_MOBILE).map(a => (
                  <MonthChip key={a.id} appt={a} apptTypes={apptTypes} onClick={() => onPickCell?.(d, 10)} />
                ))}
                {list.length > CHIPS_MOBILE && (
                  <div className="text-[9px] text-ink-500 font-medium pl-0.5">+{list.length - CHIPS_MOBILE}</div>
                )}
              </div>
              <div className="hidden sm:block space-y-0.5">
                {list.slice(0, CHIPS_DESKTOP).map(a => (
                  <MonthChip key={a.id} appt={a} apptTypes={apptTypes} onClick={() => onOpenAppt(a)} />
                ))}
                {list.length > CHIPS_DESKTOP && (
                  <div className="text-[10px] text-ink-500 font-medium pl-0.5">+{list.length - CHIPS_DESKTOP} เพิ่ม</div>
                )}
              </div>
            </div>
          );
        })}
      </div>
    </Card>
  );
}

// Single appt chip on the month grid — colored band on the left, time +
// truncated prospect name.
function MonthChip({ appt, apptTypes, onClick }) {
  const t = apptTypes.find(x => x.id === appt.type) || { label: '?', color: '#94A3B8', tint: '#F1F5F9' };
  const effStatus = _effectiveStatus(appt);
  const cancelled = effStatus === 'cancelled';
  const completed = effStatus === 'completed';
  const time = formatTime(appt.start);
  // DL-case detection mirrors ApptCard: a dl_prospect_id is the modern
  // path; the legacy users.dl_id path still works for older rows. We
  // surface the DL's name in parens so the user sees whose case it is
  // without opening the modal.
  const me = window.__currentUser?.id;
  const isDlCase = !!appt.dlProspectId || (!!appt.dlId && !!me && appt.dlId !== me);
  const dlLabel = isDlCase && appt.dlName ? appt.dlName : null;
  // Monthly running index of this appt's type — "1 ครีม, 2 พี่กัน, 3 ..."
  // Same scoping as ApptCard: personal cases only, FU excluded.
  let progressBadge = null;
  if (!isDlCase && appt.type !== 'FU') {
    const idx = window.__emphasisCaseIndex?.get(appt.id);
    if (idx) {
      progressBadge = (
        <span className="text-[8px] sm:text-[9px] font-bold leading-none flex-shrink-0 num"
              style={{ color: cancelled ? '#94A3B8' : t.color }}
              title={`${t.label} ครั้งที่ ${idx} ของเดือนนี้`}>
          {idx}
        </span>
      );
    }
  }
  return (
    <button
      data-appt-chip
      onClick={(e) => { e.stopPropagation(); onClick(); }}
      className="w-full flex items-center gap-1 px-1 sm:px-1.5 py-0.5 rounded text-left overflow-hidden"
      style={{
        background: cancelled ? '#F1F5F9' : t.tint,
        borderLeft: `2px solid ${cancelled ? '#94A3B8' : t.color}`,
        opacity: cancelled ? 0.6 : 1,
      }}
      title={`${time} ${t.label} · ${appt.prospect || '—'}${dlLabel ? ` (DL: ${dlLabel})` : ''}`}>
      {(appt._isClass || appt.type === 'CL') && <EmphasisLogo className="w-3 h-3" />}
      <span className="text-[8px] sm:text-[9px] font-bold flex-shrink-0 num" style={{ color: cancelled ? '#94A3B8' : t.color }}>
        {time}
      </span>
      {progressBadge}
      <span className={`text-[9px] sm:text-[10px] font-semibold text-ink-900 truncate ${cancelled ? 'line-through' : ''}`}>
        {appt.prospect || t.label}
      </span>
      {dlLabel && (
        <span className="text-[8px] sm:text-[9px] font-medium text-ink-500 truncate flex-shrink min-w-0"
              title={`เคสของ DL: ${dlLabel}`}>
          ({dlLabel})
        </span>
      )}
      {completed && <span className="text-[8px] text-emerald-600 flex-shrink-0">✓</span>}
    </button>
  );
}

// Day-detail modal — opens when the user taps a cell on the month grid.
// Lists every appt scheduled for that Bangkok date (sorted by time) with
// the type chip, prospect/topic, owner pill, and quick "เปิด" affordance.
// A footer button still lets the user spin up a new appt for that day.
function DayDetailModal({ open, date, monthAppts, apptTypes, leadSources, onClose, onOpenAppt, onCreateNew }) {
  if (!date) return null;

  // Bucket appts whose Bangkok date matches the picked cell.
  const dayKey = (() => {
    const d = new Date(date);
    return `${d.getFullYear()}-${d.getMonth()}-${d.getDate()}`;
  })();
  const list = (monthAppts || [])
    .filter(a => {
      if (!a.scheduled_at) return false;
      const bkk = new Date(new Date(a.scheduled_at).getTime() + 7 * 3600 * 1000);
      return `${bkk.getUTCFullYear()}-${bkk.getUTCMonth()}-${bkk.getUTCDate()}` === dayKey;
    })
    .sort((a, b) => a.scheduled_at.localeCompare(b.scheduled_at));

  const TH_DOW_FULL = ['อาทิตย์','จันทร์','อังคาร','พุธ','พฤหัสบดี','ศุกร์','เสาร์'];
  const TH_MO_FULL  = ['มกราคม','กุมภาพันธ์','มีนาคม','เมษายน','พฤษภาคม','มิถุนายน','กรกฎาคม','สิงหาคม','กันยายน','ตุลาคม','พฤศจิกายน','ธันวาคม'];

  // Bangkok midnight of `date` — used to decide if "+ สร้างนัด" should be
  // shown (past days are read-only).
  const isPastDay = (() => {
    const d = new Date(date);
    const todayBkk = new Date(Date.now() + 7 * 3600 * 1000);
    const dateStart = new Date(d.getFullYear(), d.getMonth(), d.getDate()).getTime();
    const todayStart = Date.UTC(todayBkk.getUTCFullYear(), todayBkk.getUTCMonth(), todayBkk.getUTCDate()) - 7 * 3600 * 1000;
    return dateStart < todayStart;
  })();

  const tally = list.reduce((acc, a) => {
    const es = _effectiveStatus(a);
    if (es === 'completed') acc.done++;
    else if (es === 'cancelled') acc.cancelled++;
    else if (es === 'followup') acc.followup++;
    else acc.pending++;
    return acc;
  }, { done: 0, pending: 0, followup: 0, cancelled: 0 });

  return (
    <Modal open={open} onClose={onClose} size="lg"
      title={
        <div className="flex items-center gap-2">
          <div className="w-9 h-9 rounded-lg bg-brand-50 text-brand-700 flex items-center justify-center font-bold text-base num">
            {date.getDate()}
          </div>
          <div>
            <div className="text-sm font-semibold text-ink-900">
              วัน{TH_DOW_FULL[date.getDay()]} ที่ {date.getDate()} {TH_MO_FULL[date.getMonth()]} {date.getFullYear() + 543}
            </div>
            <div className="text-[11px] text-ink-400 num">
              {list.length} เคส
              {tally.done > 0 && <span> · ปิด {tally.done}</span>}
              {tally.pending > 0 && <span> · ค้าง {tally.pending}</span>}
              {tally.followup > 0 && <span> · FU {tally.followup}</span>}
            </div>
          </div>
        </div>
      }
      footer={
        <div className="flex items-center justify-between">
          <span className="text-[11px] text-ink-400">แตะเคส → แก้ไข</span>
          <div className="flex items-center gap-2">
            <Btn variant="ghost" size="sm" onClick={onClose}>ปิด</Btn>
            {!isPastDay && (
              <Btn variant="brand" size="sm"
                icon={<Icon name="plus" className="w-3.5 h-3.5" />}
                onClick={onCreateNew}>
                สร้างนัดใหม่
              </Btn>
            )}
          </div>
        </div>
      }>
      {list.length === 0 ? (
        <div className="text-center py-12 text-sm text-ink-400">
          <div className="text-4xl mb-2">📅</div>
          <div>ยังไม่มีเคสในวันนี้</div>
          {!isPastDay && (
            <div className="text-[11px] text-ink-400 mt-1">กด "สร้างนัดใหม่" ด้านล่างเพื่อเพิ่ม</div>
          )}
        </div>
      ) : (
        <div className="max-h-[440px] overflow-y-auto scroll-thin -mx-1 px-1 space-y-3">
          {(() => {
            // Group by start time so same-time appts share one row.
            const groups = [];
            const byTime = new Map();
            for (const a of list) {
              const k = a.start;
              if (!byTime.has(k)) { byTime.set(k, []); groups.push(k); }
              byTime.get(k).push(a);
            }
            const me = window.__currentUser?.id;
            const meName = window.__currentUser?.name || 'ฉัน';
            const partnerName = window.__currentUser?.partner_name || 'คู่ร่วมธุรกิจ';
            return groups.map(k => {
              const items = byTime.get(k);
              return (
                <div key={k} className="flex gap-2.5">
                  {/* time gutter */}
                  <div className="w-12 flex-shrink-0 pt-1.5 text-right">
                    <div className="text-[13px] font-bold text-ink-700 num leading-none">{formatTime(k)}</div>
                    {items.length > 1 && <div className="text-[10px] text-ink-400 num mt-0.5">{items.length} นัด</div>}
                  </div>
                  {/* cards for this time slot */}
                  <div className="flex-1 min-w-0 space-y-1.5 border-l-2 border-ink-100 pl-2.5">
                    {items.map(a => {
                      const t = apptTypes.find(x => x.id === a.type) || { label: '?', color: '#94A3B8', tint: '#F1F5F9', full: '—' };
                      const src = leadSources?.find(s => s.id === a.source);
                      const status = window.STATUS?.find(s => s.id === _effectiveStatus(a));
                      const isDl = !!a.dlProspectId || (!!a.dlId && a.dlId !== me);
                      const ownerLabel = isDl && a.dlName ? `DL: ${a.dlName}` : a.isPartner ? `★ ${partnerName}` : `★ ${meName}`;
                      const idx = window.__emphasisCaseIndex?.get(a.id);
                      const ownerShort = isDl && a.dlName ? `DL: ${a.dlName}` : a.isPartner ? partnerName : meName;
                      return (
                        <button key={a.id} onClick={() => onOpenAppt(a)}
                          className="w-full flex items-start gap-2.5 p-2 rounded-lg border border-ink-100 bg-white hover:shadow-card hover:border-ink-200 transition-all text-left">
                          {(a._isClass || a.type === 'CL')
                            ? <EmphasisLogo className="w-8 h-8 mt-0.5" />
                            : <span className="w-8 h-8 rounded-md flex items-center justify-center text-white text-[10px] font-bold flex-shrink-0 mt-0.5" style={{ background: t.color }}>{t.label}</span>}
                          <div className="flex-1 min-w-0">
                            {/* Name — full, allowed to wrap so it never gets cut off */}
                            <div className="flex items-start gap-1.5">
                              {idx ? <span className="text-[10px] font-bold leading-none px-1.5 py-0.5 rounded flex-shrink-0 num mt-0.5" style={{ background: t.color, color: 'white' }} title={`${t.label} ครั้งที่ ${idx} ของเดือนนี้`}>{idx}</span> : null}
                              <span className="text-[14px] font-bold text-ink-900 leading-snug">{a.prospect || t.full}</span>
                            </div>
                            {/* Meta — everything else, compact on one line */}
                            <div className="text-[10.5px] text-ink-400 flex items-center gap-1 flex-wrap mt-0.5">
                              <span className="num">{Math.round((a.dur || 1) * 60)}น.</span>
                              <span className="text-ink-300">·</span>
                              <span className={isDl ? 'text-amber-700 font-semibold' : a.isPartner ? 'text-emerald-700 font-semibold' : ''}>{ownerShort}</span>
                              {src && <><span className="text-ink-300">·</span><span className="inline-flex items-center gap-0.5"><span className="w-1.5 h-1.5 rounded-full" style={{ background: src.color }} />{src.label}</span></>}
                            </div>
                          </div>
                          {status && <Pill color={status.color} tint={status.tint} size="xs">{status.label}</Pill>}
                          <Icon name="chevR" className="w-3.5 h-3.5 text-ink-300 flex-shrink-0 mt-1.5" />
                        </button>
                      );
                    })}
                  </div>
                </div>
              );
            });
          })()}
        </div>
      )}
    </Modal>
  );
}

function ActivityLog({ appts }) {
  const { apptTypes, leadSources } = useConfig();
  // Build event entries from appointments
  const events = useMemo(() => {
    const list = [];
    appts.forEach(a => {
      const t = apptTypes.find(x => x.id === a.type);
      const s = STATUS.find(x => x.id === a.status);
      const src = leadSources.find(x => x.id === a.source);
      list.push({
        day: a.day,
        time: a.start,
        type: t,
        status: s,
        source: src,
        prospect: a.prospect,
        notes: a.notes,
      });
    });
    return list.sort((a, b) => (a.day - b.day) || (a.time - b.time));
  }, [appts]);

  return (
    <Card pad={false}>
      <div className="px-5 pt-4 pb-2 flex items-center justify-between">
        <div>
          <h3 className="text-sm font-semibold text-ink-800">Activity Log สัปดาห์นี้</h3>
          <p className="text-[11px] text-ink-400">Auto-synced จาก calendar · Leader เห็นแบบ real-time</p>
        </div>
        <button className="text-xs font-medium text-brand-600 hover:text-brand-700">ดูทั้งหมด →</button>
      </div>
      <div className="px-5 pb-5">
        <div className="overflow-x-auto scroll-thin">
          <table className="w-full text-xs min-w-[640px]">
            <thead>
              <tr className="text-ink-400 text-[10px] uppercase tracking-wider">
                <th className="text-left font-medium py-2 pr-3">When</th>
                <th className="text-left font-medium py-2 pr-3">Type</th>
                <th className="text-left font-medium py-2 pr-3">Prospect</th>
                <th className="text-left font-medium py-2 pr-3">Source</th>
                <th className="text-left font-medium py-2 pr-3">Status</th>
                <th className="text-left font-medium py-2 pr-3">Notes</th>
              </tr>
            </thead>
            <tbody className="divide-y divide-ink-100">
              {events.slice(0, 6).map((e, i) => {
                const ty = e.type || { color: '#94A3B8', tint: '#F1F5F9', label: '—' };
                const st = e.status || { color: '#94A3B8', tint: '#F1F5F9', label: '—' };
                return (
                <tr key={i} className="hover:bg-ink-50/50">
                  <td className="py-2.5 pr-3 num text-ink-700">{DAYS_FULL[e.day]?.split(' ')[0]} · {formatTime(e.time)}</td>
                  <td className="py-2.5 pr-3">
                    <Pill color={ty.color} tint={ty.tint}>{ty.label}</Pill>
                  </td>
                  <td className="py-2.5 pr-3 font-medium text-ink-900">{e.prospect}</td>
                  <td className="py-2.5 pr-3 text-ink-600">
                    {e.source ? (
                      <span className="inline-flex items-center gap-1.5">
                        <span className="w-1.5 h-1.5 rounded-full" style={{ background: e.source.color }} />
                        {e.source.label}
                      </span>
                    ) : <span className="text-ink-300">—</span>}
                  </td>
                  <td className="py-2.5 pr-3">
                    <Pill color={st.color} tint={st.tint}>{st.label}</Pill>
                  </td>
                  <td className="py-2.5 pr-3 text-ink-500 truncate max-w-[200px]">{e.notes || '—'}</td>
                </tr>
                );
              })}
            </tbody>
          </table>
        </div>
      </div>
    </Card>
  );
}

window.CalendarView = CalendarView;

// ============== Type summary tile (clickable) ==============
// Splits the case counts into:
//   - primary's own cases (default — isPartner=false, not DL)
//   - partner's own cases (isPartner=true) — only when a partner exists
//   - DL-owned (dl_id / dl_prospect_id set)
// so the user can see at a glance how many of each type belong to whom.
function TypeStatTile({ type, appts, onClick }) {
  const me = window.__currentUser?.id;
  const meName = (window.__currentUser?.name || '').split(' ')[0] || 'ฉัน';
  const partnerName = window.__currentUser?.partner_name || null;
  const isDlAppt = (a) => !!a.dlProspectId || (!!a.dlId && !!me && a.dlId !== me);
  const ownAppts     = appts.filter(a => !isDlAppt(a) && !a.isPartner);
  const partnerAppts = appts.filter(a => !isDlAppt(a) &&  a.isPartner);
  const dlAppts      = appts.filter(a => isDlAppt(a));

  const tally = (list) => {
    const completed = list.filter(a => a.status === 'completed').length;
    const pending   = list.filter(a => a.status === 'pending').length;
    const followup  = list.filter(a => a.status === 'followup').length;
    const total = list.length;
    return { total, completed, pending, followup, closeRate: total > 0 ? Math.round(completed / total * 100) : 0 };
  };
  const totalT   = tally(appts);
  const ownT     = tally(ownAppts);
  const partnerT = tally(partnerAppts);
  const dlT      = tally(dlAppts);

  const isFU = type.id === 'FU';
  return (
    <button onClick={onClick}
      // FU tile drops the ownership breakdown so it's naturally shorter
      // than BM/BI/6W — pin it to the top of the row (self-start) so the
      // tile hugs its content instead of stretching to match siblings.
      className={`group relative text-left bg-white rounded-2xl border border-ink-100 hover:border-ink-200 hover:shadow-lift transition-all overflow-hidden ${isFU ? 'self-start' : ''}`}>
      {/* Top color band */}
      <div className="h-1" style={{ background: type.color }} />
      <div className="p-4">
        <div className="flex items-center gap-2 mb-3">
          <div className="w-8 h-8 rounded-lg flex items-center justify-center text-white font-bold text-xs" style={{ background: type.color }}>
            {type.label}
          </div>
          <div className="min-w-0 flex-1">
            <div className="text-sm font-semibold text-ink-900 truncate">{type.full}</div>
            <div className="text-[10px] text-ink-400 truncate">{type.desc}</div>
          </div>
          <Icon name="chevR" className="w-3.5 h-3.5 text-ink-300 group-hover:text-ink-700 group-hover:translate-x-0.5 transition-all" />
        </div>

        {/* Total headline */}
        <div className="flex items-baseline gap-2 mb-2">
          <span className="text-3xl font-semibold num text-ink-900">{totalT.total}</span>
          <span className="text-xs text-ink-400">cases · {totalT.closeRate}% close</span>
        </div>

        {/* Combined status mini bar */}
        <div className="flex h-1.5 rounded-full overflow-hidden bg-ink-100 mb-3">
          <div style={{ width: totalT.total ? `${totalT.completed/totalT.total*100}%` : 0, background: '#10B981' }} />
          <div style={{ width: totalT.total ? `${totalT.pending/totalT.total*100}%`   : 0, background: '#F59E0B' }} />
          <div style={{ width: totalT.total ? `${totalT.followup/totalT.total*100}%`  : 0, background: '#2F5DE5' }} />
        </div>

        {/* FU appts are conceptually a "general activity log" — splitting
            them into own / partner / DL adds noise without value (a follow
            up topic doesn't really belong to a downline like a BM does).
            For FU we only render the total + progress bar above; everything
            else (the ownership breakdown) is suppressed.

            All other types keep the full split:
              ★ ส่วนตัว                              23 เคส
              ──────────────────────────────
              👥 คู่ร่วมธุรกิจ   3  |  🟡 DL   8
        */}
        {!isFU && (
        <div className="border-t border-ink-100 pt-2.5 space-y-2">
          {/* Primary + Partner — equal sizing in a 2-col row when a partner
              exists, otherwise primary takes the full row. Smaller than the
              old text-2xl so DL underneath has room to be even smaller. */}
          <div className={partnerName ? 'grid grid-cols-2 gap-2' : ''}>
            <div className="flex items-center gap-1.5 min-w-0">
              <span className="text-amber-500 text-xs leading-none">★</span>
              <span className="text-[10px] uppercase tracking-wider text-ink-500 font-semibold truncate">
                {partnerName ? meName : 'ส่วนตัว'}
              </span>
              <span className="ml-auto flex items-baseline gap-1">
                <span className="text-lg font-bold num text-ink-900">{ownT.total}</span>
                <span className="text-[10px] text-ink-400">เคส</span>
              </span>
            </div>

            {partnerName && (
              <div className="flex items-center gap-1.5 min-w-0 pl-2 border-l border-ink-100">
                <span className="text-emerald-500 text-xs leading-none">★</span>
                <span className="text-[10px] uppercase tracking-wider text-ink-500 font-semibold truncate">
                  {partnerName.split(' ')[0]}
                </span>
                <span className="ml-auto flex items-baseline gap-1">
                  <span className="text-lg font-bold num" style={{ color: partnerT.total > 0 ? '#0F8B5E' : '#9DAAC4' }}>{partnerT.total}</span>
                  <span className="text-[10px] text-ink-400">เคส</span>
                </span>
              </div>
            )}
          </div>

          {/* DL — smaller secondary row, separated by a faint divider. */}
          <div className="pt-1.5 border-t border-ink-100/60 flex items-center gap-1.5">
            <span className="inline-block w-1.5 h-1.5 rounded-full" style={{ background: '#FFB400' }} />
            <span className="text-[10px] text-ink-500">DL</span>
            <span className="ml-auto flex items-baseline gap-0.5">
              <span className="text-xs font-semibold num" style={{ color: dlT.total > 0 ? '#B87C00' : '#9DAAC4' }}>{dlT.total}</span>
              <span className="text-[9px] text-ink-400">เคส</span>
            </span>
          </div>
        </div>
        )}
      </div>
    </button>
  );
}

// ============== Follow-up watchlist card ==============
function FollowUpCard({ appts, onOpenAppt }) {
  const { apptTypes } = useConfig();
  // Default to "today" so the user sees their immediate workload first.
  const [tab, setTab] = useState('today');

  // appts now includes the whole month (not just the viewed week), so
  // a.day relative to viewWeekStart isn't reliable for comparing dates.
  // Use scheduled_at to detect "today in Bangkok" and to sort.
  const todayRange = useMemo(() => {
    const now = new Date();
    const bkk = new Date(now.getTime() + 7 * 3600 * 1000);
    const y = bkk.getUTCFullYear(), m = bkk.getUTCMonth(), d = bkk.getUTCDate();
    const start = new Date(Date.UTC(y, m, d) - 7 * 3600 * 1000);
    const end = new Date(start.getTime() + 24 * 3600 * 1000);
    return { startMs: start.getTime(), endMs: end.getTime() };
  }, []);
  const isToday = (a) => {
    if (!a.scheduled_at) return false;
    const t = new Date(a.scheduled_at).getTime();
    return t >= todayRange.startMs && t < todayRange.endMs;
  };

  const sortByTime = (a, b) => {
    if (a.scheduled_at && b.scheduled_at) return a.scheduled_at.localeCompare(b.scheduled_at);
    return (a.day - b.day) || (a.start - b.start);
  };
  const pending  = appts.filter(a => a.status === 'pending').sort(sortByTime);
  const followup = appts.filter(a => a.status === 'followup').sort(sortByTime);
  const today    = appts.filter(a =>
    (a.status === 'pending' || a.status === 'followup') && isToday(a)
  ).sort(sortByTime);
  const list = tab === 'today' ? today : tab === 'pending' ? pending : followup;

  return (
    // Use a raw flex column instead of <Card> so the inner list can shrink to
    // match the height of the left column (the 4 type-stat tiles). Without
    // this the card's natural content height is taller than the tiles, and
    // the row looks lopsided.
    <div className="bg-white rounded-2xl shadow-card border border-ink-100 h-full flex flex-col">
      <div className="px-5 pt-4 pb-3 flex items-center justify-between flex-shrink-0">
        <div className="flex items-center gap-2">
          <div className="w-7 h-7 rounded-md bg-gold-50 text-gold-600 flex items-center justify-center">
            <Icon name="bell" className="w-3.5 h-3.5" />
          </div>
          <div>
            <h3 className="text-sm font-semibold text-ink-800">ต้องตามต่อ</h3>
            <p className="text-[10px] text-ink-400">รายชื่อที่ต้องติดตาม</p>
          </div>
        </div>
        <span className="text-[11px] num font-semibold text-rose-500">{pending.length + followup.length} เคส</span>
      </div>

      {/* Tabs */}
      <div className="px-5 pb-2 flex items-center gap-1 flex-shrink-0">
        <button onClick={() => setTab('today')}
          className={`flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[11px] font-semibold transition-all ${tab === 'today' ? 'bg-rose-100 text-rose-700' : 'text-ink-500 hover:bg-ink-50'}`}>
          <span className="w-1.5 h-1.5 rounded-full bg-rose-500" />
          วันนี้ <span className="num">{today.length}</span>
        </button>
        <button onClick={() => setTab('pending')}
          className={`flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[11px] font-semibold transition-all ${tab === 'pending' ? 'bg-gold-100 text-gold-700' : 'text-ink-500 hover:bg-ink-50'}`}>
          <span className="w-1.5 h-1.5 rounded-full bg-gold-500" />
          Pending <span className="num">{pending.length}</span>
        </button>
        <button onClick={() => setTab('followup')}
          className={`flex items-center gap-1.5 px-2.5 py-1 rounded-md text-[11px] font-semibold transition-all ${tab === 'followup' ? 'bg-brand-100 text-brand-700' : 'text-ink-500 hover:bg-ink-50'}`}>
          <span className="w-1.5 h-1.5 rounded-full bg-brand-500" />
          Follow-up <span className="num">{followup.length}</span>
        </button>
      </div>

      {/* List — flex-1 + min-h-0 lets it shrink to whatever space remains
          after the header/tabs, instead of stretching the card beyond the
          left column's height. */}
      <div className="px-3 pb-3 flex-1 min-h-0 overflow-y-auto scroll-thin">
        {list.length === 0 ? (
          <div className="text-center py-8 text-[11px] text-ink-400">
            {tab === 'today' ? 'ไม่มีนัดของวันนี้ 🎉' : 'ไม่มีรายการที่ต้องตาม 🎉'}
          </div>
        ) : (
          <ul className="space-y-1">
            {list.map(a => {
              const t = apptTypes.find(x => x.id === a.type) || { label: '?', color: '#94A3B8' };
              // Overdue when the appt's end time is in the past — works
              // for any week now that the list spans the whole month.
              const apptEndMs = a.scheduled_at
                ? new Date(a.scheduled_at).getTime() + Math.round((a.dur || 1) * 3600 * 1000)
                : Infinity;
              const overdue = apptEndMs < Date.now();
              return (
                <li key={a.id}>
                  <button onClick={() => onOpenAppt(a)}
                    className="w-full flex items-center gap-2.5 p-2 rounded-lg hover:bg-ink-50 transition-colors text-left group">
                    <div className="w-9 h-9 rounded-md flex-shrink-0 flex flex-col items-center justify-center text-white text-[9px] font-bold leading-none" style={{ background: t.color }}>
                      <div>{t.label}</div>
                      <div className="text-[8px] opacity-80 mt-0.5">{formatTime(a.start)}</div>
                    </div>
                    <div className="flex-1 min-w-0">
                      <div className="text-xs font-semibold text-ink-900 truncate">
                        {a.prospect}
                        {a.dlProspectId && a.dlName && a.type !== 'FU' && (
                          <span className="ml-1 text-[10px] font-medium text-ink-500">({a.dlName})</span>
                        )}
                      </div>
                      <div className="text-[10px] text-ink-400 num">{(() => {
                        // Date label off scheduled_at — works across weeks
                        // (a.day is relative to the viewed week only).
                        const TH_DAYS = ['อา.','จ.','อ.','พ.','พฤ.','ศ.','ส.'];
                        const TH_MO = ['ม.ค.','ก.พ.','มี.ค.','เม.ย.','พ.ค.','มิ.ย.','ก.ค.','ส.ค.','ก.ย.','ต.ค.','พ.ย.','ธ.ค.'];
                        if (a.scheduled_at) {
                          const bkk = new Date(new Date(a.scheduled_at).getTime() + 7 * 3600 * 1000);
                          return `${TH_DAYS[bkk.getUTCDay()]} · ${bkk.getUTCDate()} ${TH_MO[bkk.getUTCMonth()]}`;
                        }
                        return `${DAYS_FULL[a.day]?.split(' ')[0]} · ${DAYS_FULL[a.day]?.split(' ')[1]}`;
                      })()}</div>
                    </div>
                    {overdue && <Pill color="#EF4444" tint="#FEE2E2" size="xs">ผ่านไปแล้ว</Pill>}
                    <Icon name="chevR" className="w-3 h-3 text-ink-300 group-hover:text-ink-700 group-hover:translate-x-0.5 transition-all flex-shrink-0" />
                  </button>
                </li>
              );
            })}
          </ul>
        )}
      </div>
    </div>
  );
}

// ============== Type detail modal ==============
function TypeDetailModal({ open, onClose, typeId, appts, onOpenAppt }) {
  const { apptTypes, leadSources } = useConfig();
  // Owner-split tab — "all" | "own" | "partner" | "dl". Reset whenever the
  // modal opens or switches type so the user always lands on "all" first.
  const [ownerTab, setOwnerTab] = useState('all');
  useEffect(() => { if (open) setOwnerTab('all'); }, [open, typeId]);

  if (!typeId) return null;
  const type = apptTypes.find(t => t.id === typeId);
  if (!type) return null;

  const me = window.__currentUser?.id;
  const meName = (window.__currentUser?.name || '').split(' ')[0] || 'ฉัน';
  const partnerName = window.__currentUser?.partner_name || null;
  const isDlAppt = (a) => !!a.dlProspectId || (!!a.dlId && !!me && a.dlId !== me);

  const allForType = appts
    .filter(a => a.type === typeId)
    .sort((a, b) => (a.day - b.day) || (a.start - b.start));

  // Counts per owner bucket so the tab pills show numbers.
  const own     = allForType.filter(a => !isDlAppt(a) && !a.isPartner);
  const partner = allForType.filter(a => !isDlAppt(a) &&  a.isPartner);
  const dl      = allForType.filter(a =>  isDlAppt(a));

  const list = ownerTab === 'own'     ? own
            : ownerTab === 'partner'  ? partner
            : ownerTab === 'dl'       ? dl
            :                            allForType;

  const grouped = {
    pending:   list.filter(a => a.status === 'pending'),
    completed: list.filter(a => a.status === 'completed'),
    followup:  list.filter(a => a.status === 'followup'),
    cancelled: list.filter(a => a.status === 'cancelled'),
  };

  return (
    <Modal open={open} onClose={onClose} size="lg" title={
      <div className="flex items-center gap-2.5">
        <div className="w-9 h-9 rounded-lg flex items-center justify-center text-white font-bold" style={{ background: type.color }}>
          {type.label}
        </div>
        <div>
          <div className="text-sm font-semibold text-ink-900">{type.full}</div>
          <div className="text-[11px] text-ink-400">
            {list.length} เคส
            {ownerTab !== 'all' && (
              <span className="ml-1">
                · {ownerTab === 'own' ? (partnerName ? `${meName} (ส่วนตัว)` : 'ส่วนตัว')
                  : ownerTab === 'partner' ? partnerName
                  : 'DL'}
              </span>
            )}
            <span className="ml-1">· สัปดาห์นี้</span>
          </div>
        </div>
      </div>
    } footer={
      <div className="flex items-center justify-between">
        <span className="text-[11px] text-ink-400">คลิกแถวเพื่อแก้ไขนัด</span>
        <Btn variant="ghost" size="sm" onClick={onClose}>ปิด</Btn>
      </div>
    }>
      {/* Owner-split tabs — separates own / partner / DL cases. Tab counts
          are over ALL cases of this type, regardless of which tab is active,
          so the user can switch contexts without losing the totals. */}
      <div className="flex items-center gap-1.5 mb-3 flex-wrap">
        {[
          { id: 'all',     label: 'ทั้งหมด',                     n: allForType.length, color: '#0F1E38' },
          { id: 'own',     label: `★ ${partnerName ? meName : 'ส่วนตัว'}`, n: own.length,        color: '#B87C00' },
          ...(partnerName ? [{ id: 'partner', label: `★ ${partnerName.split(' ')[0]}`, n: partner.length, color: '#0F8B5E' }] : []),
          { id: 'dl',      label: '🟡 DL',                       n: dl.length,         color: '#B87C00' },
        ].map(t => {
          const active = ownerTab === t.id;
          return (
            <button key={t.id} onClick={() => setOwnerTab(t.id)}
              className="px-2.5 py-1.5 rounded-lg text-[11px] font-semibold transition-all flex items-center gap-1 border-2"
              style={active
                ? { borderColor: t.color, background: t.color + '12', color: t.color }
                : { borderColor: '#E6EAF3', background: 'white', color: '#445576' }}>
              <span>{t.label}</span>
              <span className="num text-[10px] opacity-80">{t.n}</span>
            </button>
          );
        })}
      </div>

      {/* Status summary chips (re-counted on the active owner tab) */}
      <div className="grid grid-cols-4 gap-2 mb-4">
        {[
          { id: 'pending',   label: 'Pending',   color: '#F59E0B', tint: '#FFF6E0' },
          { id: 'completed', label: 'Completed', color: '#10B981', tint: '#E7F8F1' },
          { id: 'followup',  label: 'Follow-up', color: '#2F5DE5', tint: '#EEF4FF' },
          { id: 'cancelled', label: 'Cancelled', color: '#94A3B8', tint: '#F1F5F9' },
        ].map(s => (
          <div key={s.id} className="rounded-lg p-2 text-center" style={{ background: s.tint }}>
            <div className="text-xl font-semibold num" style={{ color: s.color }}>{grouped[s.id].length}</div>
            <div className="text-[10px] font-medium" style={{ color: s.color }}>{s.label}</div>
          </div>
        ))}
      </div>

      {/* List */}
      <div className="max-h-[360px] overflow-y-auto scroll-thin -mx-2">
        {list.length === 0 ? (
          <div className="text-center py-12 text-sm text-ink-400">
            {ownerTab === 'all'
              ? 'ยังไม่มีเคสในสัปดาห์นี้'
              : ownerTab === 'dl'
              ? 'ยังไม่มีเคส DL ในสัปดาห์นี้'
              : ownerTab === 'partner'
              ? `ยังไม่มีเคสของ ${partnerName || 'คู่ร่วมธุรกิจ'} ในสัปดาห์นี้`
              : 'ยังไม่มีเคสส่วนตัวในสัปดาห์นี้'}
          </div>
        ) : (
          <ul className="space-y-1 px-2">
            {list.map(a => {
              const src = leadSources.find(x => x.id === a.source) || { label: '—', color: '#94A3B8' };
              const status = window.STATUS.find(x => x.id === a.status);
              return (
                <li key={a.id}>
                  <button onClick={() => onOpenAppt(a)}
                    className="w-full flex items-center gap-3 p-2.5 rounded-lg hover:bg-ink-50 transition-colors text-left">
                    {/* Date pill */}
                    <div className="w-12 text-center flex-shrink-0">
                      <div className="text-[10px] uppercase font-bold text-ink-400 leading-none">{DAYS_FULL[a.day]?.split(' ')[0]}</div>
                      <div className="text-base font-semibold num text-ink-900 mt-1">{DAYS_FULL[a.day]?.split(' ')[1]}</div>
                    </div>
                    <div className="w-px h-9 bg-ink-100 flex-shrink-0" />
                    {/* Body */}
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center gap-2 mb-0.5 flex-wrap">
                        <span className="text-sm font-semibold text-ink-900 truncate">{a.prospect}</span>
                        {/* Owner marker so the "ทั้งหมด" tab still distinguishes
                            personal vs partner vs DL at a glance. */}
                        {isDlAppt(a)
                          ? <Pill color="#B87C00" tint="#FFECB8" size="xs">DL{a.dlName ? `: ${a.dlName}` : ''}</Pill>
                          : a.isPartner
                          ? <Pill color="#0F8B5E" tint="#E7F8F1" size="xs">★ {window.__currentUser?.partner_name || 'คู่ร่วมธุรกิจ'}</Pill>
                          : null}
                        <span className="text-[11px] num text-ink-500 flex-shrink-0 ml-auto">{formatTime(a.start)}</span>
                      </div>
                      <div className="flex items-center gap-2 text-[11px] text-ink-500">
                        <span className="inline-flex items-center gap-1">
                          <span className="w-1.5 h-1.5 rounded-full" style={{ background: src.color }} />
                          {src.label}
                        </span>
                        {a.notes && (
                          <>
                            <span className="text-ink-300">·</span>
                            <span className="truncate text-ink-400">{a.notes}</span>
                          </>
                        )}
                      </div>
                    </div>
                    {/* Status */}
                    <Pill color={status.color} tint={status.tint} size="xs">{status.label}</Pill>
                  </button>
                </li>
              );
            })}
          </ul>
        )}
      </div>
    </Modal>
  );
}

// Expose the summary widgets so the standalone "สรุปเคส" view (views/cases.jsx)
// can reuse them without duplicating the markup. Calendar.jsx loads before
// cases.jsx in index.html, so window globals are wired up by the time
// cases.jsx runs.
window.TypeStatTile = TypeStatTile;
window.FollowUpCard = FollowUpCard;
window.MobilePendingList = MobilePendingList;
window.TypeDetailModal = TypeDetailModal;
