// PromoEvents.jsx — admin CRUD for promo_events. Each row in the
// table is one event (one-shot or recurring). Editor modal builds the
// iCal RRULE string from a friendly UI (freq + weekdays + interval +
// end condition).

const promoEventFromRow = (r) => ({
  id: r.id,
  title: r.title,
  description: r.description || '',
  couponCode: r.coupon_code || '',
  startsAt: r.starts_at,
  endsAt: r.ends_at || null,
  allDay: !!r.all_day,
  rrule: r.rrule || '',
  location: r.location || '',
  url: r.url || '',
  // null in DB = no reminder; positive integer = minutes before start.
  reminderMinutes: (r.reminder_minutes === null || r.reminder_minutes === undefined)
    ? null
    : Number(r.reminder_minutes),
  archived: !!r.archived,
});
const promoEventToRow = (o) => ({
  id: o.id,
  title: (o.title || '').trim(),
  description: (o.description || '').trim() || null,
  coupon_code: (o.couponCode || '').trim() || null,
  starts_at: o.startsAt,
  ends_at: o.endsAt || null,
  all_day: !!o.allDay,
  rrule: (o.rrule || '').trim() || null,
  location: (o.location || '').trim() || null,
  url: (o.url || '').trim() || null,
  reminder_minutes: (o.reminderMinutes === null || o.reminderMinutes === undefined)
    ? null
    : Number(o.reminderMinutes),
  archived: !!o.archived,
  updated_at: new Date().toISOString(),
});

const PromoEvents = () => {
  const [events, setEvents] = window.useSupaList('promo_events', {
    fromRow: promoEventFromRow,
    toRow: promoEventToRow,
    initial: () => window.SAMPLE_PROMO_EVENTS || [],
  });
  const [editing, setEditing] = useState(null); // event or 'new'
  const [toast, setToast] = useState('');
  const [showArchived, setShowArchived] = useState(false);

  const sortedActive = [...events]
    .filter(e => !e.archived)
    .sort((a, b) => (a.startsAt || '').localeCompare(b.startsAt || ''));
  const archived = [...events]
    .filter(e => e.archived)
    .sort((a, b) => (b.startsAt || '').localeCompare(a.startsAt || ''));

  const save = (draft) => {
    if (!draft.title.trim() || !draft.startsAt) return;
    if (draft.id && events.some(e => e.id === draft.id)) {
      setEvents(es => es.map(e => e.id === draft.id ? { ...draft } : e));
      setToast('Event updated');
    } else {
      const id = window.crypto?.randomUUID
        ? window.crypto.randomUUID()
        : 'pe-' + Date.now().toString(36) + '-' + Math.random().toString(36).slice(2, 8);
      setEvents(es => [...es, { ...draft, id, archived: false }]);
      setToast('Event added');
    }
    setEditing(null);
  };

  const archive = (id) => {
    setEvents(es => es.map(e => e.id === id ? { ...e, archived: true } : e));
    setToast('Event archived (won\'t ship to subscribers)');
  };
  const restore = (id) => {
    setEvents(es => es.map(e => e.id === id ? { ...e, archived: false } : e));
    setToast('Event restored');
  };
  const remove = (id) => {
    if (!confirm('Delete this event forever? Past .ics polls already pushed it to subscribers; deleting just stops future polls from including it.')) return;
    setEvents(es => es.filter(e => e.id !== id));
    setToast('Event deleted');
  };

  return (
    <div className="portal-page-wide" style={{ maxWidth: 1080 }}>
      <div className="portal-page-header">
        <div>
          <h1 className="portal-page-title">Promo Calendar</h1>
          <div className="portal-page-subtitle">
            Events here ship to every active subscriber's phone calendar via{' '}
            <a href="https://promo-calendar.snap-a-box.com" target="_blank" rel="noreferrer"
              style={{ color: 'var(--fg-1)', fontWeight: 500 }}>
              promo-calendar.snap-a-box.com
            </a>. iOS polls roughly hourly; Android polls when Google Calendar syncs.
          </div>
        </div>
        <PBtn variant="primary" icon="plus" onClick={() => setEditing('new')}>Add event</PBtn>
      </div>

      <div className="portal-card" style={{ padding: 0, marginBottom: 16 }}>
        <table className="portal-table">
          <thead>
            <tr>
              <th>Event</th>
              <th style={{ width: 180 }}>When</th>
              <th style={{ width: 140 }}>Recurrence</th>
              <th style={{ width: 100 }}>Coupon</th>
              <th style={{ width: 80 }}></th>
            </tr>
          </thead>
          <tbody>
            {sortedActive.length === 0 ? (
              <tr><td colSpan={5} style={{ padding: 40, textAlign: 'center', color: 'var(--fg-3)', fontSize: 13 }}>
                No active events. Add one to start pushing to subscribers.
              </td></tr>
            ) : sortedActive.map(ev => (
              <tr key={ev.id} onClick={() => setEditing(ev)} style={{ cursor: 'pointer' }}>
                <td>
                  <div style={{ fontSize: 13.5, fontWeight: 500, color: 'var(--fg-1)' }}>{ev.title}</div>
                  {ev.description && (
                    <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginTop: 2, whiteSpace: 'nowrap', overflow: 'hidden', textOverflow: 'ellipsis', maxWidth: 480 }}>
                      {ev.description}
                    </div>
                  )}
                </td>
                <td style={{ fontSize: 12.5, color: 'var(--fg-2)', fontFamily: 'var(--font-num)' }}>
                  {formatStartLine(ev)}
                </td>
                <td style={{ fontSize: 12, color: 'var(--fg-2)' }}>
                  {ev.rrule ? rruleSummary(ev.rrule) : <span style={{ color: 'var(--fg-3)' }}>One-shot</span>}
                </td>
                <td>
                  {ev.couponCode ? (
                    <span style={{
                      fontSize: 11.5, fontWeight: 600,
                      fontFamily: 'var(--font-num)', letterSpacing: '0.04em',
                      padding: '2px 8px', borderRadius: 6,
                      background: '#FDE68A', color: '#92400E',
                    }}>{ev.couponCode}</span>
                  ) : <span style={{ color: 'var(--fg-3)' }}>—</span>}
                </td>
                <td onClick={e => e.stopPropagation()}>
                  <div className="row-actions" style={{ opacity: 1 }}>
                    <button onClick={() => setEditing(ev)} title="Edit"><PIcon name="edit" size={13} /></button>
                    <button onClick={() => archive(ev.id)} title="Archive"><PIcon name="archive" size={13} /></button>
                  </div>
                </td>
              </tr>
            ))}
          </tbody>
        </table>
      </div>

      <button onClick={() => setShowArchived(s => !s)} style={{
        fontSize: 12.5, color: 'var(--fg-3)',
        padding: '6px 10px', borderRadius: 7,
      }}>
        {showArchived ? `Hide archived (${archived.length})` : `Show archived (${archived.length})`}
      </button>

      {showArchived && archived.length > 0 && (
        <div className="portal-card" style={{ padding: 0, marginTop: 12 }}>
          <table className="portal-table">
            <tbody>
              {archived.map(ev => (
                <tr key={ev.id} style={{ opacity: 0.7 }}>
                  <td style={{ fontSize: 13, color: 'var(--fg-2)' }}>{ev.title}</td>
                  <td style={{ fontSize: 12, color: 'var(--fg-3)', fontFamily: 'var(--font-num)' }}>
                    {formatStartLine(ev)}
                  </td>
                  <td>
                    <div className="row-actions" style={{ opacity: 1 }}>
                      <button onClick={() => restore(ev.id)} title="Restore"><PIcon name="check" size={13} /></button>
                      <button onClick={() => remove(ev.id)} title="Delete forever"><PIcon name="trash" size={13} /></button>
                    </div>
                  </td>
                </tr>
              ))}
            </tbody>
          </table>
        </div>
      )}

      {editing && (
        <PromoEventEditor
          initial={editing === 'new' ? null : editing}
          onClose={() => setEditing(null)}
          onSave={save}
        />
      )}

      <PToast message={toast} open={!!toast} onClose={() => setToast('')} />
    </div>
  );
};

// ------------------------------------------------------------------
// Event editor — collects all VEVENT-shaped fields plus a friendly
// recurrence builder that compiles to an iCal RRULE string.
// ------------------------------------------------------------------
const PromoEventEditor = ({ initial, onClose, onSave }) => {
  const init = initial || {
    title: '', description: '', couponCode: '',
    startsAt: defaultStartIso(),
    endsAt: null,
    allDay: false,
    rrule: '',
    location: '', url: '',
    reminderMinutes: null,
  };
  const parsed = parseRrule(init.rrule || '');

  const [title, setTitle]       = useState(init.title);
  const [description, setDesc]  = useState(init.description);
  const [couponCode, setCoupon] = useState(init.couponCode);
  const [allDay, setAllDay]     = useState(init.allDay);
  const [startsAt, setStartsAt] = useState(init.startsAt);
  const [endsAt, setEndsAt]     = useState(init.endsAt);
  const [location, setLocation] = useState(init.location);
  const [url, setUrl]           = useState(init.url);
  const [freq, setFreq]         = useState(parsed.freq);          // 'NONE' | 'DAILY' | 'WEEKLY' | 'MONTHLY' | 'YEARLY'
  const [weekdays, setWeekdays] = useState(parsed.weekdays);      // ['MO','TU'] etc
  const [interval, setInterval] = useState(parsed.interval);      // int >= 1
  const [endMode, setEndMode]   = useState(parsed.endMode);       // 'never' | 'until' | 'count'
  const [untilDate, setUntilDate] = useState(parsed.until);
  const [count, setCount]       = useState(parsed.count);
  // null = no reminder; positive integer = minutes before start.
  const [reminderMinutes, setReminderMinutes] = useState(
    init.reminderMinutes === null || init.reminderMinutes === undefined ? null : Number(init.reminderMinutes)
  );

  const canSave = title.trim().length > 0 && !!startsAt;

  const handleSave = () => {
    const rrule = buildRrule({ freq, weekdays, interval, endMode, untilDate, count });
    onSave({
      ...(initial || {}),
      id: initial?.id,
      title, description, couponCode,
      startsAt, endsAt: endsAt || null,
      allDay,
      rrule,
      location, url,
      reminderMinutes,
    });
  };

  return (
    <PModal open onClose={onClose} title={initial ? 'Edit event' : 'New event'} width={620} footer={
      <>
        <PBtn variant="secondary" onClick={onClose}>Cancel</PBtn>
        <PBtn variant="primary" onClick={handleSave} disabled={!canSave}>
          {initial ? 'Save changes' : 'Add event'}
        </PBtn>
      </>
    }>
      <div style={{ padding: '14px 18px', display: 'flex', flexDirection: 'column', gap: 16 }}>
        <PField label="Title" hint="Emoji welcome — e.g. 🍱 BOGO Classic Fried Rice">
          <input className="portal-input" value={title} onChange={e => setTitle(e.target.value)}
            style={{ height: 36 }} autoFocus />
        </PField>

        <PField label="Description" hint="Shown in the event body in the customer's calendar.">
          <textarea className="portal-input" value={description}
            onChange={e => setDesc(e.target.value)} rows={3} />
        </PField>

        <PField label="Coupon code" hint="Optional. Highlighted separately in the event body.">
          <input className="portal-input" value={couponCode}
            onChange={e => setCoupon(e.target.value.toUpperCase())}
            style={{ height: 36, width: 200, fontFamily: 'var(--font-num)', letterSpacing: '0.04em' }} />
        </PField>

        <PField label="When">
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, marginBottom: 8 }}>
            <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'var(--fg-2)' }}>
              <input type="checkbox" checked={allDay} onChange={e => setAllDay(e.target.checked)} />
              All day
            </label>
          </div>
          <div style={{ display: 'flex', gap: 12, alignItems: 'center', flexWrap: 'wrap' }}>
            <div>
              <div style={{ fontSize: 11, color: 'var(--fg-3)', marginBottom: 4 }}>Start</div>
              <input
                className="portal-input"
                type={allDay ? 'date' : 'datetime-local'}
                value={toDtInput(startsAt, allDay)}
                onChange={e => setStartsAt(fromDtInput(e.target.value, allDay))}
                style={{ height: 36 }}
              />
            </div>
            <div>
              <div style={{ fontSize: 11, color: 'var(--fg-3)', marginBottom: 4 }}>End <span style={{ color: 'var(--fg-3)' }}>(optional)</span></div>
              <input
                className="portal-input"
                type={allDay ? 'date' : 'datetime-local'}
                value={toDtInput(endsAt, allDay)}
                onChange={e => setEndsAt(e.target.value ? fromDtInput(e.target.value, allDay) : null)}
                style={{ height: 36 }}
              />
            </div>
          </div>
          <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginTop: 6 }}>
            Timed events render in each customer's device-local time. All-day events span the full day.
          </div>
        </PField>

        <PField label="Repeat">
          <div style={{ display: 'flex', gap: 6, flexWrap: 'wrap', marginBottom: 10 }}>
            {[
              { id: 'NONE',    label: 'No repeat' },
              { id: 'DAILY',   label: 'Daily' },
              { id: 'WEEKLY',  label: 'Weekly' },
              { id: 'MONTHLY', label: 'Monthly' },
              { id: 'YEARLY',  label: 'Yearly' },
            ].map(opt => (
              <button key={opt.id} type="button" onClick={() => setFreq(opt.id)} style={{
                padding: '6px 12px', borderRadius: 999,
                background: freq === opt.id ? 'var(--fg-1)' : 'var(--bg-sunken)',
                color: freq === opt.id ? '#FFFFFF' : 'var(--fg-2)',
                fontSize: 12.5, fontWeight: 500, border: 'none', cursor: 'pointer',
              }}>{opt.label}</button>
            ))}
          </div>

          {freq === 'WEEKLY' && (
            <div style={{ display: 'flex', gap: 6, marginBottom: 10 }}>
              {[
                { id: 'SU', label: 'Sun' },
                { id: 'MO', label: 'Mon' },
                { id: 'TU', label: 'Tue' },
                { id: 'WE', label: 'Wed' },
                { id: 'TH', label: 'Thu' },
                { id: 'FR', label: 'Fri' },
                { id: 'SA', label: 'Sat' },
              ].map(d => {
                const on = weekdays.includes(d.id);
                return (
                  <button key={d.id} type="button" onClick={() => {
                    setWeekdays(prev => prev.includes(d.id) ? prev.filter(x => x !== d.id) : [...prev, d.id]);
                  }} style={{
                    padding: '6px 10px', borderRadius: 999,
                    background: on ? 'var(--fg-1)' : 'var(--bg-sunken)',
                    color: on ? '#FFFFFF' : 'var(--fg-2)',
                    fontSize: 12.5, fontWeight: 500, border: 'none', cursor: 'pointer',
                    fontFamily: 'var(--font-num)',
                  }}>{d.label}</button>
                );
              })}
            </div>
          )}

          {freq !== 'NONE' && (
            <div style={{ display: 'flex', alignItems: 'center', gap: 8, marginBottom: 12 }}>
              <span style={{ fontSize: 13, color: 'var(--fg-2)' }}>Every</span>
              <input
                type="number" min={1} max={99}
                className="portal-input"
                value={interval}
                onChange={e => setInterval(Math.max(1, Number(e.target.value) || 1))}
                style={{ height: 32, width: 70, textAlign: 'center', fontFamily: 'var(--font-num)' }}
              />
              <span style={{ fontSize: 13, color: 'var(--fg-2)' }}>{freqLabel(freq, interval)}</span>
            </div>
          )}

          {freq !== 'NONE' && (
            <div>
              <div style={{ fontSize: 11.5, color: 'var(--fg-3)', marginBottom: 6 }}>Ends</div>
              <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
                {['never', 'until', 'count'].map(m => (
                  <label key={m} style={{ display: 'inline-flex', alignItems: 'center', gap: 5, fontSize: 13, color: 'var(--fg-2)' }}>
                    <input type="radio" name="endmode" checked={endMode === m} onChange={() => setEndMode(m)} />
                    {m === 'never' ? 'Never' : m === 'until' ? 'On date' : 'After N times'}
                  </label>
                ))}
                {endMode === 'until' && (
                  <input
                    type="date"
                    className="portal-input"
                    value={untilDate || ''}
                    onChange={e => setUntilDate(e.target.value)}
                    style={{ height: 32 }}
                  />
                )}
                {endMode === 'count' && (
                  <input
                    type="number" min={1} max={999}
                    className="portal-input"
                    value={count}
                    onChange={e => setCount(Math.max(1, Number(e.target.value) || 1))}
                    style={{ height: 32, width: 80, textAlign: 'center', fontFamily: 'var(--font-num)' }}
                  />
                )}
              </div>
            </div>
          )}
        </PField>

        <PField label="Reminder" hint="Optional. Fires a heads-up notification in the customer's calendar before the event.">
          <div style={{ display: 'flex', alignItems: 'center', gap: 12, flexWrap: 'wrap' }}>
            <label style={{ display: 'inline-flex', alignItems: 'center', gap: 6, fontSize: 13, color: 'var(--fg-2)' }}>
              <input
                type="checkbox"
                checked={reminderMinutes !== null}
                onChange={e => setReminderMinutes(e.target.checked ? (allDay ? 1440 : 60) : null)}
              />
              Remind customers
            </label>
            {reminderMinutes !== null && (
              <select
                className="portal-input"
                value={String(reminderMinutes)}
                onChange={e => setReminderMinutes(Number(e.target.value))}
                style={{ height: 32 }}
              >
                <option value="10">10 minutes before</option>
                <option value="30">30 minutes before</option>
                <option value="60">1 hour before</option>
                <option value="120">2 hours before</option>
                <option value="1440">1 day before</option>
                <option value="2880">2 days before</option>
                <option value="10080">1 week before</option>
              </select>
            )}
          </div>
        </PField>

        <PField label="Location" hint="Optional. Defaults blank — customers know where you are.">
          <input className="portal-input" value={location}
            onChange={e => setLocation(e.target.value)}
            style={{ height: 36 }} />
        </PField>

        <PField label="URL" hint="Optional. Becomes the event link in the customer's calendar.">
          <input className="portal-input" value={url}
            onChange={e => setUrl(e.target.value)}
            style={{ height: 36 }} />
        </PField>
      </div>
    </PModal>
  );
};

// `Field` is the shared `PField` atom in portal-shared.jsx (see HANDBOOK §9).

// ------------------------------------------------------------------
// RRULE compile + parse. We restrict the surface area to FREQ /
// BYDAY / INTERVAL / UNTIL / COUNT — enough for the v1 patterns
// (every Tuesday, every 2 weeks, first 4 occurrences, etc).
// ------------------------------------------------------------------
function buildRrule({ freq, weekdays, interval, endMode, untilDate, count }) {
  if (freq === 'NONE') return '';
  const parts = ['FREQ=' + freq];
  if (freq === 'WEEKLY' && weekdays && weekdays.length > 0) {
    parts.push('BYDAY=' + weekdays.join(','));
  }
  if (interval && interval > 1) {
    parts.push('INTERVAL=' + interval);
  }
  if (endMode === 'until' && untilDate) {
    // RFC 5545: UNTIL must be UTC for floating events; we use end-of-day.
    parts.push('UNTIL=' + untilDate.replace(/-/g, '') + 'T235959Z');
  } else if (endMode === 'count' && count) {
    parts.push('COUNT=' + count);
  }
  return parts.join(';');
}

function parseRrule(rrule) {
  const out = {
    freq: 'NONE',
    weekdays: [],
    interval: 1,
    endMode: 'never',
    until: '',
    count: 10,
  };
  if (!rrule) return out;
  const parts = rrule.split(';').map(p => p.split('=')).reduce((m, [k, v]) => { m[k] = v; return m; }, {});
  if (parts.FREQ) out.freq = parts.FREQ;
  if (parts.BYDAY) out.weekdays = parts.BYDAY.split(',').filter(Boolean);
  if (parts.INTERVAL) out.interval = Math.max(1, Number(parts.INTERVAL) || 1);
  if (parts.UNTIL) {
    out.endMode = 'until';
    // 20260601T235959Z → 2026-06-01
    out.until = parts.UNTIL.slice(0, 4) + '-' + parts.UNTIL.slice(4, 6) + '-' + parts.UNTIL.slice(6, 8);
  } else if (parts.COUNT) {
    out.endMode = 'count';
    out.count = Math.max(1, Number(parts.COUNT) || 1);
  }
  return out;
}

function rruleSummary(rrule) {
  if (!rrule) return 'One-shot';
  const p = parseRrule(rrule);
  if (p.freq === 'NONE') return 'One-shot';
  const fr = { DAILY: 'day', WEEKLY: 'week', MONTHLY: 'month', YEARLY: 'year' }[p.freq] || p.freq.toLowerCase();
  const every = p.interval > 1 ? `Every ${p.interval} ${fr}s` : `Every ${fr}`;
  if (p.freq === 'WEEKLY' && p.weekdays.length > 0) {
    return `${every}: ${p.weekdays.join(', ')}`;
  }
  return every;
}

function freqLabel(freq, n) {
  const base = { DAILY: 'day', WEEKLY: 'week', MONTHLY: 'month', YEARLY: 'year' }[freq] || '';
  return n === 1 ? base : base + 's';
}

function formatStartLine(ev) {
  if (!ev.startsAt) return '';
  const d = new Date(ev.startsAt);
  if (ev.allDay) {
    return d.toLocaleDateString([], { month: 'short', day: 'numeric', year: 'numeric' });
  }
  return d.toLocaleString([], { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' });
}

function defaultStartIso() {
  // Next noon, local time.
  const d = new Date();
  d.setDate(d.getDate() + 1);
  d.setHours(12, 0, 0, 0);
  return d.toISOString();
}

function toDtInput(iso, allDay) {
  if (!iso) return '';
  const d = new Date(iso);
  const pad = (n) => String(n).padStart(2, '0');
  if (allDay) {
    return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate());
  }
  return d.getFullYear() + '-' + pad(d.getMonth() + 1) + '-' + pad(d.getDate())
    + 'T' + pad(d.getHours()) + ':' + pad(d.getMinutes());
}

function fromDtInput(value, allDay) {
  if (!value) return null;
  if (allDay) {
    // Treat as midnight local.
    const [y, m, d] = value.split('-').map(Number);
    return new Date(y, m - 1, d, 0, 0, 0).toISOString();
  }
  return new Date(value).toISOString();
}

window.PromoEvents = PromoEvents;
