// StorePhone.jsx — Admin Portal "Store Phone" page.
//
// Reads phone_calls rows upserted by the phone-calls-webhook Edge
// Function as Quo (OpenPhone-backed) delivers call.completed events.
// Bi-weekly view (same pay-period scheme as Daily Report / Delivery)
// with two purposes:
//   1. Audit: which calls came in, who answered, which got missed.
//   2. Accountability: for every missed inbound, was there an outbound
//      callback to the same number within `call_match_window_minutes`?
//      Defaults to 30 min, editable from Settings → General.
//
// Eventual roadmap: rolled-up "missed call" and "late callback" counts
// per day, surfaced on Daily Report for bonus calculation. v1 ships
// the raw table only.

const { useState: useSp, useEffect: useEffectSp, useMemo: useMemoSp } = React;

// "(346) 666-7702" — Quo gives us +13466667702, this prettifies for display.
const fmtPhoneSp = (e164) => {
  if (!e164) return '—';
  const s = String(e164).replace(/^\+1/, '').replace(/\D/g, '');
  if (s.length === 10) return `(${s.slice(0, 3)}) ${s.slice(3, 6)}-${s.slice(6)}`;
  return e164;
};

const fmtDur = (sec) => {
  const n = Number(sec) || 0;
  if (n < 60) return `${n}s`;
  const m = Math.floor(n / 60);
  const s = n % 60;
  return s ? `${m}m ${s}s` : `${m}m`;
};

const fmtTimeSp = (iso) => {
  if (!iso) return '';
  const d = new Date(iso);
  if (isNaN(d.getTime())) return '';
  return d.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' });
};

// "YYYY-MM-DD" (restaurant local).
const isoKeySp = (d) =>
  `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;

const DOW_ABBR_SP = ['SUN', 'MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT'];

// Compute the callback metadata for each missed inbound: the FIRST
// outbound call to the same external_number that starts AFTER the
// missed call's started_at. Returns { calledBack: boolean, minutes,
// withinWindow }.
// Window comes from restaurants.call_match_window_minutes.
function computeCallbacks(rows, windowMinutes) {
  // Sort by start time so we can match nearest-future outbound first.
  const byNumber = new Map();
  rows.forEach(r => {
    const k = r.external_number;
    if (!k) return;
    if (!byNumber.has(k)) byNumber.set(k, []);
    byNumber.get(k).push(r);
  });
  byNumber.forEach(list => list.sort((a, b) => new Date(a.started_at) - new Date(b.started_at)));

  const result = new Map();  // row.id -> callback info
  rows.forEach(missed => {
    if (missed.direction !== 'incoming' || missed.answered) return;
    const list = byNumber.get(missed.external_number) || [];
    const missedAt = new Date(missed.started_at).getTime();
    // Find FIRST outbound after this missed call.
    const cb = list.find(r =>
      r.direction === 'outgoing' &&
      new Date(r.started_at).getTime() > missedAt
    );
    if (!cb) {
      result.set(missed.id, { calledBack: false });
      return;
    }
    const minutes = Math.round((new Date(cb.started_at).getTime() - missedAt) / 60000);
    result.set(missed.id, {
      calledBack: true,
      minutes,
      withinWindow: minutes <= windowMinutes,
      cbId: cb.id,
    });
  });
  return result;
}

const StorePhone = ({ payAnchor }) => {
  const [calls, setCalls] = useSp([]);
  const [loading, setLoading] = useSp(true);
  const [windowMinutes, setWindowMinutes] = useSp(30);
  // Mirrored draft of windowMinutes so the user can type freely (e.g.
  // clear the input to type "45") without committing until blur/Enter.
  // Synced from windowMinutes whenever it changes from outside.
  const [windowDraft, setWindowDraft] = useSp('30');
  useEffectSp(() => { setWindowDraft(String(windowMinutes)); }, [windowMinutes]);
  const [filter, setFilter] = useSp('all');  // 'all' | 'missed' | 'incoming' | 'outgoing'
  const [periodIdx, setPeriodIdx] = useSp(0);  // 0 = current bi-weekly window, -1 = previous, etc.

  // Persist the callback window to the restaurants row. Clamps 1–1440 min
  // and rounds non-integers — same rule the Settings page used. Called on
  // blur/Enter from the inline editor in the header.
  const saveWindowMinutes = async () => {
    const v = Math.max(1, Math.min(1440, parseInt(windowDraft, 10) || 30));
    if (v === windowMinutes) { setWindowDraft(String(v)); return; }
    setWindowMinutes(v);
    setWindowDraft(String(v));
    if (!window.supa) return;
    await window.supa.from('restaurants')
      .update({ call_match_window_minutes: v })
      .eq('id', window.RESTAURANT_ID);
  };

  // Compute the bi-weekly window from payAnchor + periodIdx. Same convention
  // as DailyReport / Delivery so a "period" line up across pages.
  const period = useMemoSp(() => {
    if (!payAnchor || !window.getPayPeriod) return null;
    const cur = window.getPayPeriod(payAnchor, new Date());
    if (!cur) return null;
    const anchor = new Date(cur.start);
    anchor.setDate(anchor.getDate() + periodIdx * 14);
    return window.getPayPeriod(payAnchor, anchor);
  }, [payAnchor, periodIdx]);

  // Load the window's worth of calls + the restaurant's match-window setting.
  useEffectSp(() => {
    if (!window.supa) return;
    if (!period) return;
    let cancelled = false;
    setLoading(true);
    // period.start / period.end are Date objects at local midnight
    // (getPayPeriod returns Date, not string — same as DailyReport).
    const startIso = period.start.toISOString();
    const endDay = new Date(period.end);
    endDay.setDate(endDay.getDate() + 1);  // inclusive of end day
    const endIso = endDay.toISOString();
    Promise.all([
      window.supa
        .from('phone_calls')
        .select('*')
        .eq('restaurant_id', window.RESTAURANT_ID)
        .gte('started_at', startIso)
        .lt('started_at', endIso)
        .order('started_at', { ascending: false }),
      window.supa
        .from('restaurants')
        .select('call_match_window_minutes')
        .eq('id', window.RESTAURANT_ID)
        .single(),
    ]).then(([callsRes, restRes]) => {
      if (cancelled) return;
      setCalls(callsRes.data || []);
      if (restRes.data?.call_match_window_minutes) {
        setWindowMinutes(restRes.data.call_match_window_minutes);
      }
      setLoading(false);
    });
    return () => { cancelled = true; };
  }, [period?.start, period?.end]);

  const callbacksMap = useMemoSp(
    () => computeCallbacks(calls, windowMinutes),
    [calls, windowMinutes],
  );

  // KPIs for the period.
  const stats = useMemoSp(() => {
    const inbound = calls.filter(c => c.direction === 'incoming');
    const missed = inbound.filter(c => !c.answered);
    const calledBack = missed.filter(c => callbacksMap.get(c.id)?.calledBack);
    const withinWindow = missed.filter(c => callbacksMap.get(c.id)?.withinWindow);
    return {
      total: calls.length,
      inbound: inbound.length,
      missed: missed.length,
      missedNoCallback: missed.length - calledBack.length,
      missedLateCallback: calledBack.length - withinWindow.length,
    };
  }, [calls, callbacksMap]);

  const filtered = useMemoSp(() => {
    if (filter === 'all') return calls;
    if (filter === 'missed') return calls.filter(c => c.direction === 'incoming' && !c.answered);
    if (filter === 'incoming') return calls.filter(c => c.direction === 'incoming');
    if (filter === 'outgoing') return calls.filter(c => c.direction === 'outgoing');
    return calls;
  }, [calls, filter]);

  // Group rows by day for readability.
  const byDay = useMemoSp(() => {
    const groups = new Map();  // 'YYYY-MM-DD' -> rows[]
    filtered.forEach(c => {
      const k = isoKeySp(new Date(c.started_at));
      if (!groups.has(k)) groups.set(k, []);
      groups.get(k).push(c);
    });
    return [...groups.entries()].sort((a, b) => b[0].localeCompare(a[0]));
  }, [filtered]);

  return (
    <div className="portal-page-wide" style={{ maxWidth: 1200 }}>
      <PPageHeader
        title="Store Phone"
        subtitle={<span style={{ display: 'flex', alignItems: 'center', gap: 6, flexWrap: 'wrap' }}>
          <span>Bi-weekly call log from the store line ({fmtPhoneSp('+13466667702')}). Missed inbound calls count as called-back-in-time if the outbound to that number is within</span>
          <input
            type="number"
            min="1"
            max="1440"
            value={windowDraft}
            onChange={e => setWindowDraft(e.target.value)}
            onBlur={saveWindowMinutes}
            onKeyDown={e => { if (e.key === 'Enter') e.currentTarget.blur(); }}
            style={{
              width: 56, height: 26,
              padding: '0 6px',
              border: '1px solid var(--border-1)',
              borderRadius: 6,
              fontSize: 13, fontWeight: 600,
              fontFamily: 'var(--font-num)',
              textAlign: 'right',
              color: 'var(--fg-1)',
              background: '#FFFFFF',
            }}
            title="Callback window in minutes. Saves when you click away or press Enter."
          />
          <span>minutes.</span>
        </span>}
        right={period ? (
          <div style={{ display: 'flex', alignItems: 'center', gap: 10 }}>
            <PBtn variant="secondary" size="sm" icon="chevronL" onClick={() => setPeriodIdx(i => i - 1)}>Previous</PBtn>
            <div style={{ fontSize: 13, color: 'var(--fg-1)', fontWeight: 500, whiteSpace: 'nowrap' }}>
              <span style={{ fontFamily: 'var(--font-num)' }}>{fmtDate(period.start)}</span>
              <span style={{ color: 'var(--fg-3)' }}> – </span>
              <span style={{ fontFamily: 'var(--font-num)' }}>{fmtDate(period.end)}</span>
              {periodIdx === 0 && (
                <span style={{ marginLeft: 8, fontSize: 11, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>Current</span>
              )}
            </div>
            <PBtn
              variant="secondary"
              size="sm"
              onClick={() => setPeriodIdx(i => i + 1)}
              disabled={periodIdx >= 0}
              style={{ opacity: periodIdx >= 0 ? 0.4 : 1 }}
            >Next <PIcon name="chevronR" size={14} /></PBtn>
          </div>
        ) : null}
        crewLead
      />

      {/* KPI strip */}
      <div style={{
        display: 'grid',
        gridTemplateColumns: 'repeat(5, minmax(0, 1fr))',
        gap: 12,
        marginBottom: 18,
      }}>
        <SpKpi label="Total calls" value={stats.total} />
        <SpKpi label="Inbound" value={stats.inbound} />
        <SpKpi label="Missed inbound" value={stats.missed} accent={stats.missed > 0 ? 'warn' : null} />
        <SpKpi label="No callback" value={stats.missedNoCallback} accent={stats.missedNoCallback > 0 ? 'danger' : null} />
        <SpKpi label={`Callback > ${windowMinutes}m`} value={stats.missedLateCallback} accent={stats.missedLateCallback > 0 ? 'warn' : null} />
      </div>

      {/* Filter chips */}
      <div style={{ display: 'flex', gap: 8, marginBottom: 16, flexWrap: 'wrap' }}>
        {[
          { id: 'all', label: 'All' },
          { id: 'missed', label: 'Missed only' },
          { id: 'incoming', label: 'Inbound' },
          { id: 'outgoing', label: 'Outbound' },
        ].map(opt => {
          const active = filter === opt.id;
          return (
            <button
              key={opt.id}
              onClick={() => setFilter(opt.id)}
              style={{
                padding: '6px 12px', borderRadius: 999,
                fontSize: 12.5, fontWeight: 500,
                background: active ? 'var(--fg-1)' : 'var(--bg-sunken)',
                color: active ? '#FFFFFF' : 'var(--fg-2)',
                border: '1px solid ' + (active ? 'var(--fg-1)' : 'transparent'),
                cursor: 'pointer',
              }}
            >{opt.label}</button>
          );
        })}
      </div>

      {/* Table */}
      {loading ? (
        <div className="portal-empty"><div style={{ fontSize: 14, color: 'var(--fg-2)' }}>Loading calls…</div></div>
      ) : filtered.length === 0 ? (
        <div className="portal-empty"><div style={{ fontSize: 14, color: 'var(--fg-2)' }}>No calls in this window.</div></div>
      ) : (
        byDay.map(([dayKey, rows]) => {
          const [y, m, d] = dayKey.split('-').map(Number);
          const dayDate = new Date(y, m - 1, d);
          const dow = DOW_ABBR_SP[dayDate.getDay()];
          const dayLabel = dayDate.toLocaleDateString('en-US', { month: 'short', day: 'numeric' });
          return (
            <div key={dayKey} style={{ marginBottom: 18 }}>
              <div style={{
                fontSize: 11, fontWeight: 700, color: 'var(--fg-3)',
                textTransform: 'uppercase', letterSpacing: '0.08em',
                padding: '0 0 8px 4px',
              }}>{dow} · {dayLabel}</div>
              <div style={{
                background: '#FFFFFF',
                border: '1px solid var(--border-2)',
                borderRadius: 10,
                overflow: 'hidden',
              }}>
                <table style={{ borderCollapse: 'collapse', width: '100%' }}>
                  <thead>
                    <tr style={{ borderBottom: '1px solid var(--border-2)', background: '#FAFAF9' }}>
                      {['Time', 'Number', 'Direction', 'Duration', 'Status', `Callback (≤ ${windowMinutes}m)`].map((h, i) => (
                        <th key={i} style={{ textAlign: 'left', padding: '10px 14px', fontSize: 11, fontWeight: 600, color: 'var(--fg-2)', textTransform: 'uppercase', letterSpacing: '0.06em', whiteSpace: 'nowrap' }}>{h}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {rows.map(c => {
                      const cb = callbacksMap.get(c.id);
                      const isMissed = c.direction === 'incoming' && !c.answered;
                      return (
                        <tr key={c.id} style={{ borderBottom: '1px solid var(--border-2)' }}>
                          <td style={{ padding: '10px 14px', fontSize: 13, color: 'var(--fg-2)', fontFamily: 'var(--font-num)', whiteSpace: 'nowrap' }}>{fmtTimeSp(c.started_at)}</td>
                          <td style={{ padding: '10px 14px', fontSize: 13, fontWeight: 500, color: 'var(--fg-1)', fontFamily: 'var(--font-num)', whiteSpace: 'nowrap' }}>{fmtPhoneSp(c.external_number)}</td>
                          <td style={{ padding: '10px 14px' }}>
                            <span style={{
                              display: 'inline-block',
                              fontSize: 11.5, fontWeight: 600,
                              padding: '2px 8px', borderRadius: 999,
                              background: c.direction === 'incoming' ? '#E0F2FE' : '#EDE9FE',
                              color: c.direction === 'incoming' ? '#075985' : '#5B21B6',
                              letterSpacing: '-0.005em',
                            }}>{c.direction === 'incoming' ? 'In' : 'Out'}</span>
                          </td>
                          <td style={{ padding: '10px 14px', fontSize: 13, color: 'var(--fg-2)', fontFamily: 'var(--font-num)', whiteSpace: 'nowrap' }}>
                            {c.duration_seconds > 0 ? fmtDur(c.duration_seconds) : <span style={{ color: 'var(--fg-3)' }}>—</span>}
                          </td>
                          <td style={{ padding: '10px 14px' }}>
                            {c.answered ? (
                              <span style={{
                                display: 'inline-flex', alignItems: 'center', gap: 5,
                                fontSize: 12, fontWeight: 500, color: 'var(--success)',
                                padding: '3px 10px', borderRadius: 999,
                                background: 'var(--success-bg)',
                              }}>
                                <PIcon name="check" size={12} color="var(--success)" />
                                Answered
                              </span>
                            ) : isMissed ? (
                              <span style={{
                                display: 'inline-flex', alignItems: 'center', gap: 5,
                                fontSize: 12, fontWeight: 500, color: 'var(--danger)',
                                padding: '3px 10px', borderRadius: 999,
                                background: 'var(--danger-bg)',
                              }}>Missed</span>
                            ) : (
                              <span style={{ fontSize: 12, color: 'var(--fg-3)' }}>{c.status}</span>
                            )}
                          </td>
                          <td style={{ padding: '10px 14px', fontSize: 12.5 }}>
                            {!isMissed ? (
                              <span style={{ color: 'var(--fg-3)' }}>—</span>
                            ) : !cb || !cb.calledBack ? (
                              <span style={{ color: 'var(--danger)', fontWeight: 500 }}>No callback</span>
                            ) : cb.withinWindow ? (
                              <span style={{ color: 'var(--success)', fontWeight: 500, fontFamily: 'var(--font-num)' }}>
                                ✓ {cb.minutes}m
                              </span>
                            ) : (
                              <span style={{ color: 'var(--warning)', fontWeight: 500, fontFamily: 'var(--font-num)' }}>
                                {cb.minutes}m (late)
                              </span>
                            )}
                          </td>
                        </tr>
                      );
                    })}
                  </tbody>
                </table>
              </div>
            </div>
          );
        })
      )}
    </div>
  );
};

const SpKpi = ({ label, value, accent }) => {
  // accent: null | 'warn' | 'danger'
  const accentColor = accent === 'danger' ? 'var(--danger)' : accent === 'warn' ? 'var(--warning)' : 'var(--fg-1)';
  return (
    <div style={{
      background: '#FFFFFF',
      border: '1px solid var(--border-2)',
      borderRadius: 10,
      padding: '12px 14px',
    }}>
      <div style={{ fontSize: 10.5, fontWeight: 600, color: 'var(--fg-3)', textTransform: 'uppercase', letterSpacing: '0.06em' }}>{label}</div>
      <div style={{ fontSize: 22, fontWeight: 600, color: accentColor, fontFamily: 'var(--font-num)', marginTop: 4, letterSpacing: '-0.02em' }}>{value}</div>
    </div>
  );
};

window.StorePhone = StorePhone;
