// screen-schedule.jsx — дневная повестка расписания + правка занятия
// (как в модалке плитки сетки): время/тренер/зал, замена, отмена, продление, удаление.
(() => {
  const { useState, useEffect, useMemo } = React;

  // Дата как «YYYY-MM-DD» в локальном календаре. Никаких toISOString() от
  // локальной полуночи — в UTC+5 это съезжает на день назад и стрелки топчутся.
  function todayIso() {
    const d = new Date();
    return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
  }
  // Прибавление дней — чисто календарное, через UTC-арифметику (без сдвига зоны).
  function addDays(iso, n) {
    const [y, m, d] = iso.split('-').map(Number);
    const dt = new Date(Date.UTC(y, m - 1, d));
    dt.setUTCDate(dt.getUTCDate() + n);
    return dt.toISOString().slice(0, 10);
  }
  function dayLabel(iso) {
    const [y, m, d] = iso.split('-').map(Number);
    const dt = new Date(Date.UTC(y, m - 1, d));
    const today = todayIso();
    const base = dt.toLocaleDateString('ru-RU', { weekday: 'short', day: '2-digit', month: 'long', timeZone: 'UTC' });
    if (iso === today) return 'Сегодня · ' + base;
    if (iso === addDays(today, 1)) return 'Завтра · ' + base;
    if (iso === addDays(today, -1)) return 'Вчера · ' + base;
    return base;
  }
  const hhmm = (t) => (t ? String(t).slice(0, 5) : '');
  function teacherName(t) {
    if (!t) return '';
    if (t.is_collective) return t.collective_name || 'Коллектив';
    return [t.last_name, t.first_name].filter(Boolean).join(' ').trim() || `#${t.id}`;
  }

  window.ScheduleScreen = function ScheduleScreen() {
    const [date, setDate] = useState(todayIso());
    const [edit, setEdit] = useState(null);     // занятие в правке
    const [attend, setAttend] = useState(null); // занятие в отметке явки
    const st = window.useAsync(
      () => window.api.schedule.list({ date_from: date, date_to: date }),
      [date]
    );
    const { data, loading, error, reload } = st;
    const items = useMemo(
      () => (data || []).slice().sort((a, b) => String(a.start_time).localeCompare(String(b.start_time))),
      [data]
    );
    const isToday = date === todayIso();

    return (
      <div className="screen">
        {/* Переключатель дня + календарь (тап по дате открывает нативный пикер) */}
        <div className="card" style={{ padding: 8, marginBottom: 14 }}>
          <div className="row-between">
            <button className="topbar-action" onClick={() => setDate(addDays(date, -1))} aria-label="Предыдущий день">
              <Icon name="back" size={20} />
            </button>
            <div style={{ flex: 1, display: 'flex', flexDirection: 'column', alignItems: 'center', gap: 2 }}>
              {/* Тап по дате → нативный календарь (инпут прозрачно поверх ТОЛЬКО даты). */}
              <label style={{ position: 'relative', cursor: 'pointer' }}>
                <span style={{ fontWeight: 700, textTransform: 'capitalize' }}>
                  {dayLabel(date)} <Icon name="calendar" size={14} style={{ verticalAlign: '-2px', opacity: 0.6 }} />
                </span>
                <input type="date" value={date} onChange={(e) => e.target.value && setDate(e.target.value)}
                  aria-label="Выбрать дату"
                  style={{ position: 'absolute', inset: 0, opacity: 0, width: '100%', height: '100%', border: 'none', cursor: 'pointer' }} />
              </label>
              {/* Отдельная кнопка — НЕ под инпутом, поэтому кликается без календаря. */}
              {!isToday && (
                <button type="button" className="badge accent" style={{ border: 'none', cursor: 'pointer' }}
                  onClick={() => setDate(todayIso())}>к сегодня</button>
              )}
            </div>
            <button className="topbar-action" onClick={() => setDate(addDays(date, 1))} aria-label="Следующий день"
              style={{ transform: 'rotate(180deg)' }}>
              <Icon name="back" size={20} />
            </button>
          </div>
        </div>

        {loading && <Spinner />}
        {error && <div className="empty"><span className="empty-emoji">⚠️</span>{error}
          <div style={{ marginTop: 14 }}><button className="btn btn-ghost btn-sm" onClick={reload}>Повторить</button></div>
        </div>}

        {!loading && !error && items.length === 0 && (
          <div className="empty"><span className="empty-emoji">📅</span>На этот день занятий нет</div>
        )}

        {!loading && items.map((it) => {
          const cancelled = it.status === 'cancelled';
          const cnt = Number(it.visit_count) || 0;
          return (
            <div key={it.id} className="card tap" onClick={() => setEdit(it)}
              style={cancelled ? { opacity: 0.6 } : null}>
              <div className="card-row" style={{ alignItems: 'flex-start' }}>
                <div style={{ textAlign: 'center', minWidth: 52 }}>
                  <div className="mono" style={{ fontWeight: 700, fontSize: 15 }}>{hhmm(it.start_time)}</div>
                  <div className="muted mono" style={{ fontSize: 12 }}>{hhmm(it.end_time)}</div>
                </div>
                <div className="grow">
                  <div style={{ fontWeight: 600 }} className="ellipsis">
                    {it.discipline_name || 'Занятие'}
                    {cancelled && <span className="badge danger" style={{ marginLeft: 8 }}>Отменено</span>}
                  </div>
                  <div className="subtle ellipsis" style={{ marginTop: 2 }}>
                    {[it.hall_name, it.teacher_name].filter(Boolean).join(' · ')}
                  </div>
                  {it.substitute_teacher_id && <div className="badge warn" style={{ marginTop: 6 }}>Замена</div>}
                </div>
                {!cancelled && (
                  <button className="btn btn-sm" onClick={(e) => { e.stopPropagation(); setAttend(it); }}
                    style={{ width: 'auto', flex: '0 0 auto' }}>
                    <Icon name="check" size={16} /> Явка{cnt ? ' ' + cnt : ''}
                  </button>
                )}
              </div>
            </div>
          );
        })}

        {edit && <LessonSheet item={edit} date={date} onClose={() => setEdit(null)} onChanged={() => { setEdit(null); reload(); }} />}
        {attend && <AttendancePanel item={attend} visitDate={date} onClose={() => { setAttend(null); reload(); }} />}
      </div>
    );
  };

  // ── Отметка явки ─────────────────────────────────────────
  // Рабочий список хранится ЛОКАЛЬНО: серверный ростер (постоянные + уже
  // отмеченные) + добавленные оператором. Снятие галки НЕ убирает человека из
  // списка — оператор может колебаться. Галка = есть/нет visit. «Добавить»
  // ставит клиента в список БЕЗ отметки; явку оператор проставляет сам тапом.
  function AttendancePanel({ item, visitDate, onClose }) {
    const [list, setList] = useState(null);   // null = загрузка
    const [loadErr, setLoadErr] = useState(null);
    const [busyId, setBusyId] = useState(null);
    const [query, setQuery] = useState('');
    const [results, setResults] = useState([]);
    const [searching, setSearching] = useState(false);

    useEffect(() => {
      let alive = true;
      window.api.visits.historyBySchedule(item.id, 90)
        .then((d) => { if (alive) setList((d && d.clients) || []); })
        .catch((e) => { if (alive) { setList([]); setLoadErr(e.message || 'Ошибка'); } });
      return () => { alive = false; };
    }, [item.id]);

    // Поиск для добавления (исключаем тех, кто уже в списке).
    useEffect(() => {
      const q = query.trim();
      if (q.length < 2) { setResults([]); setSearching(false); return; }
      setSearching(true);
      const t = setTimeout(async () => {
        try {
          const rows = await window.api.clients.search(q);
          const have = new Set((list || []).map((c) => c.id));
          setResults((rows || []).filter((r) => !have.has(r.id)).slice(0, 8));
        } catch { setResults([]); } finally { setSearching(false); }
      }, 320);
      return () => clearTimeout(t);
    }, [query, list]);

    const patch = (id, f) => setList((prev) => (prev || []).map((c) => (c.id === id ? { ...c, ...f } : c)));

    // Галка: отметить (создать visit) / снять (удалить visit). Клиент остаётся.
    async function toggle(c) {
      if (busyId) return;
      setBusyId(c.id);
      try {
        if (c.visit_id) {
          await window.api.visits.delete(c.visit_id);
          patch(c.id, { visit_id: null, today_visit_type: null });
        } else {
          const useSub = c.subscription_id && Number(c.visits_left) > 0;
          const r = await window.api.visits.mark({
            client_id: c.id, schedule_item_id: item.id, visit_date: visitDate,
            subscription_id: useSub ? c.subscription_id : null,
            visit_type: useSub ? 'regular' : 'debt',
          });
          patch(c.id, { visit_id: r.id, today_visit_type: useSub ? 'regular' : 'debt' });
        }
      } catch (e) {
        window.toast(e.message || 'Ошибка отметки', 'err');
      } finally { setBusyId(null); }
    }

    // Добавить найденного клиента В СПИСОК (без отметки). Подтягиваем активный
    // абонемент для подсказки «осталось N» — явку оператор ставит сам.
    async function addClient(sc) {
      if (busyId) return;
      setBusyId('add:' + sc.id);
      try {
        const full = await window.api.clients.get(sc.id).catch(() => null);
        const subs = (full && full.active_subscriptions) || [];
        const sub = subs.find((s) => Number(s.visits_left) > 0) || null;
        const entry = {
          id: sc.id, first_name: sc.first_name, last_name: sc.last_name,
          subscription_id: sub ? sub.id : null,
          subscription_type: sub ? sub.type_name : null,
          visits_left: sub ? sub.visits_left : null,
          visit_id: null, today_visit_type: null,
        };
        setList((prev) => ((prev || []).some((c) => c.id === sc.id) ? prev : [entry, ...(prev || [])]));
        setQuery(''); setResults([]);
      } catch (e) {
        window.toast(e.message || 'Ошибка', 'err');
      } finally { setBusyId(null); }
    }

    const cname = (c) => [c.last_name, c.first_name].filter(Boolean).join(' ').trim() || ('#' + c.id);
    const clients = list || [];
    const marked = clients.filter((c) => c.visit_id).length;

    return (
      <Sheet title="Отметка явки" onClose={onClose}
        footer={<button className="btn btn-primary" onClick={onClose}>Готово</button>}>
        <div className="subtle" style={{ marginBottom: 10 }}>
          {item.discipline_name} · {hhmm(item.start_time)} · отмечено {marked}/{clients.length}
        </div>

        {/* Добавить клиента поиском */}
        <div className="search-bar">
          <span className="search-ico"><Icon name="search" size={18} /></span>
          <input className="input" inputMode="search" placeholder="Добавить клиента по имени"
            value={query} onChange={(e) => setQuery(e.target.value)} />
        </div>
        {searching && <div className="muted" style={{ padding: '4px 2px 8px' }}>Поиск…</div>}
        {results.length > 0 && (
          <div className="card" style={{ padding: '2px 14px', marginBottom: 12 }}>
            {results.map((r) => (
              <button key={r.id} type="button" className="list-line" disabled={busyId === 'add:' + r.id}
                onClick={() => addClient(r)}
                style={{ width: '100%', textAlign: 'left', background: 'transparent', border: 'none',
                  borderBottom: '1px solid var(--border)', opacity: busyId === 'add:' + r.id ? 0.5 : 1 }}>
                <Icon name="plus" size={18} className="muted" />
                <div className="grow"><div style={{ fontWeight: 600 }} className="ellipsis">{cname(r)}</div></div>
              </button>
            ))}
          </div>
        )}

        {list === null && <Spinner />}
        {loadErr && <div className="badge danger" style={{ display: 'block', padding: '10px 12px' }}>{loadErr}</div>}
        {list !== null && clients.length === 0 && !query && (
          <div className="empty"><span className="empty-emoji">👥</span>
            Список пуст. Добавьте клиентов поиском выше — постоянные появятся здесь после первых посещений.</div>
        )}
        {clients.map((c) => {
          const on = !!c.visit_id;
          const debt = on && c.today_visit_type === 'debt';
          return (
            <button key={c.id} type="button" onClick={() => toggle(c)} disabled={busyId === c.id}
              className="list-line" style={{ width: '100%', textAlign: 'left', background: 'transparent',
                border: 'none', borderBottom: '1px solid var(--border)', opacity: busyId === c.id ? 0.5 : 1 }}>
              <div style={{ width: 26, height: 26, borderRadius: 7, flex: '0 0 auto',
                display: 'flex', alignItems: 'center', justifyContent: 'center',
                border: `2px solid ${on ? 'var(--accent)' : 'var(--border-2)'}`,
                background: on ? 'var(--accent)' : 'transparent', color: 'var(--on-accent)' }}>
                {on && <Icon name="check" size={16} />}
              </div>
              <div className="grow">
                <div style={{ fontWeight: 600 }} className="ellipsis">{cname(c)}</div>
                <div className="subtle">
                  {c.subscription_type ? c.subscription_type + ' · ' : ''}
                  {c.visits_left != null ? 'осталось ' + c.visits_left : 'без абонемента'}
                </div>
              </div>
              {debt && <span className="badge danger">в долг</span>}
            </button>
          );
        })}
      </Sheet>
    );
  }

  // ── Правка занятия ───────────────────────────────────────
  function LessonSheet({ item, date, onClose, onChanged }) {
    const [teachers, setTeachers] = useState(null);
    const [halls, setHalls] = useState(null);
    const [form, setForm] = useState({
      start_time: hhmm(item.start_time),
      end_time: hhmm(item.end_time),
      teacher_id: item.teacher_id || '',
      hall_id: item.hall_id || '',
    });
    const [sub, setSub] = useState({
      on: !!item.substitute_teacher_id,
      teacher_id: item.substitute_teacher_id || '',
      amount: item.substitute_amount != null ? String(item.substitute_amount) : '',
      note: item.substitute_note || '',
    });
    const [busy, setBusy] = useState(false);
    const [err, setErr] = useState(null);
    const [conflict, setConflict] = useState(null);   // soft 409 payload + pending body
    const [mode, setMode] = useState('edit');          // 'edit' | 'cancel' | 'extend' | 'delete'
    const [cancelReason, setCancelReason] = useState('');
    const [weeks, setWeeks] = useState(4);

    useEffect(() => {
      window.api.teachers.list().then((r) => setTeachers(r.items || r || [])).catch(() => setTeachers([]));
      window.api.halls.list().then((r) => setHalls(r || [])).catch(() => setHalls([]));
    }, []);

    function set(k, v) { setForm((f) => ({ ...f, [k]: v })); }

    function buildPatch() {
      const body = {};
      if (form.start_time && form.start_time !== hhmm(item.start_time)) body.start_time = form.start_time;
      if (form.end_time && form.end_time !== hhmm(item.end_time)) body.end_time = form.end_time;
      if (String(form.teacher_id) !== String(item.teacher_id || '')) body.teacher_id = Number(form.teacher_id) || null;
      if (String(form.hall_id) !== String(item.hall_id || '')) body.hall_id = Number(form.hall_id) || null;
      // Замена: отправляем ключи, только если что-то изменилось.
      const subChanged = sub.on !== !!item.substitute_teacher_id
        || String(sub.teacher_id) !== String(item.substitute_teacher_id || '')
        || String(sub.amount) !== String(item.substitute_amount ?? '')
        || (sub.note || '') !== (item.substitute_note || '');
      if (subChanged) {
        if (sub.on) {
          body.substitute_teacher_id = Number(sub.teacher_id) || null;
          body.substitute_amount = sub.amount === '' ? null : Number(sub.amount);
          body.substitute_note = sub.note || null;
        } else {
          body.substitute_teacher_id = null;
          body.substitute_amount = null;
          body.substitute_note = null;
        }
      }
      return body;
    }

    async function save(extra) {
      const body = { ...buildPatch(), ...(extra || {}) };
      if (Object.keys(body).length === 0) { onClose(); return; }
      setBusy(true); setErr(null);
      try {
        await window.api.schedule.update(item.id, body);
        window.toast('Сохранено', 'ok');
        onChanged();
      } catch (e) {
        if (e.status === 409 && (e.teacher || e.hall || e.soft)) {
          setConflict({ payload: e, body });
        } else {
          setErr(e.message || 'Ошибка сохранения');
        }
      } finally { setBusy(false); }
    }

    async function doCancel() {
      setBusy(true); setErr(null);
      try {
        await window.api.schedule.update(item.id, { status: 'cancelled', cancel_reason: cancelReason || 'Отменено оператором' });
        window.toast('Занятие отменено', 'ok');
        onChanged();
      } catch (e) { setErr(e.message || 'Ошибка'); } finally { setBusy(false); }
    }

    async function doExtend() {
      setBusy(true); setErr(null);
      try {
        await window.api.schedule.extend({ item_ids: [item.id], weeks: Number(weeks) });
        window.toast(`Продлено на ${weeks} нед.`, 'ok');
        onChanged();
      } catch (e) {
        if (e.status === 409) setErr('Конфликт расписания при продлении. Часть занятий пересекается.');
        else setErr(e.message || 'Ошибка продления');
      } finally { setBusy(false); }
    }

    async function doDelete() {
      setBusy(true); setErr(null);
      try {
        await window.api.schedule.delete(item.id);
        window.toast('Занятие удалено', 'ok');
        onChanged();
      } catch (e) { setErr(e.message || 'Ошибка удаления'); } finally { setBusy(false); }
    }

    const tOpts = teachers || [];
    const hOpts = halls || [];

    // Экран soft-конфликта (409).
    if (conflict) {
      const p = conflict.payload;
      const msg = p.message || (p.teacher ? `Тренер уже занят в это время (${p.teacher.teacher_name})`
        : p.hall ? `Зал занят в это время (${p.hall.hall_name})` : 'Пересечение в расписании');
      return (
        <Sheet title="Пересечение в расписании" onClose={() => setConflict(null)}
          footer={<>
            <button className="btn btn-ghost" onClick={() => setConflict(null)} disabled={busy}>Назад</button>
            <button className="btn btn-primary" disabled={busy}
              onClick={() => { const b = conflict.body; setConflict(null); save({ ...b, allow_overlap: true }); }}>
              Сохранить всё равно
            </button>
          </>}>
          <div className="badge warn" style={{ display: 'block', padding: '12px 14px', fontWeight: 400 }}>{msg}</div>
        </Sheet>
      );
    }

    // Подтверждение отмены — с причиной.
    if (mode === 'cancel') {
      return (
        <Sheet title="Отменить занятие" onClose={() => setMode('edit')}
          footer={<>
            <button className="btn btn-ghost" onClick={() => setMode('edit')} disabled={busy}>Назад</button>
            <button className="btn btn-danger" onClick={doCancel} disabled={busy}>
              {busy ? '…' : 'Отменить занятие'}
            </button>
          </>}>
          <div className="field">
            <label>Причина отмены</label>
            <input className="input" value={cancelReason} onChange={(e) => setCancelReason(e.target.value)}
              placeholder="Например: болезнь тренера" />
          </div>
          {err && <div className="badge danger" style={{ display: 'block', padding: '10px 12px' }}>{err}</div>}
        </Sheet>
      );
    }

    // Продление.
    if (mode === 'extend') {
      return (
        <Sheet title="Продлить занятие" onClose={() => setMode('edit')}
          footer={<>
            <button className="btn btn-ghost" onClick={() => setMode('edit')} disabled={busy}>Назад</button>
            <button className="btn btn-primary" onClick={doExtend} disabled={busy}>
              {busy ? '…' : 'Продлить'}
            </button>
          </>}>
          <div className="subtle" style={{ marginBottom: 12 }}>
            Скопировать это занятие на каждую неделю вперёд (то же время, тренер и зал).
          </div>
          <div className="field">
            <label>На сколько недель</label>
            <select className="input" value={weeks} onChange={(e) => setWeeks(Number(e.target.value))}>
              {[1, 2, 4, 8, 12].map((w) => <option key={w} value={w}>{w} нед.</option>)}
            </select>
          </div>
          {err && <div className="badge danger" style={{ display: 'block', padding: '10px 12px' }}>{err}</div>}
        </Sheet>
      );
    }

    // Подтверждение удаления.
    if (mode === 'delete') {
      return (
        <Sheet title="Удалить занятие" onClose={() => setMode('edit')}
          footer={<>
            <button className="btn btn-ghost" onClick={() => setMode('edit')} disabled={busy}>Назад</button>
            <button className="btn btn-danger" onClick={doDelete} disabled={busy}>
              {busy ? '…' : 'Удалить'}
            </button>
          </>}>
          <div className="badge danger" style={{ display: 'block', padding: '12px 14px', fontWeight: 400 }}>
            Удалить это занятие безвозвратно? Только эту дату ({hhmm(item.start_time)}).
          </div>
          {err && <div className="badge danger" style={{ display: 'block', padding: '10px 12px', marginTop: 10 }}>{err}</div>}
        </Sheet>
      );
    }

    // Основной экран правки.
    return (
      <Sheet title={item.discipline_name || 'Занятие'} onClose={onClose}
        footer={<>
          <button className="btn btn-ghost" onClick={onClose} disabled={busy}>Закрыть</button>
          <button className="btn btn-primary" onClick={() => save()} disabled={busy}>
            {busy ? 'Сохранение…' : 'Сохранить'}
          </button>
        </>}>
        <div className="subtle" style={{ marginBottom: 12 }}>
          {item.hall_name} · записано {Number(item.visit_count) || 0}{item.capacity ? '/' + item.capacity : ''}
        </div>

        <div className="btn-row">
          <div className="field grow">
            <label>Начало</label>
            <input className="input" type="time" value={form.start_time} onChange={(e) => set('start_time', e.target.value)} />
          </div>
          <div className="field grow">
            <label>Конец</label>
            <input className="input" type="time" value={form.end_time} onChange={(e) => set('end_time', e.target.value)} />
          </div>
        </div>

        <div className="field">
          <label>Тренер</label>
          <select className="input" value={form.teacher_id} onChange={(e) => set('teacher_id', e.target.value)}>
            <option value="">— не указан —</option>
            {tOpts.map((t) => <option key={t.id} value={t.id}>{teacherName(t)}</option>)}
          </select>
        </div>

        <div className="field">
          <label>Зал</label>
          <select className="input" value={form.hall_id} onChange={(e) => set('hall_id', e.target.value)}>
            <option value="">— не указан —</option>
            {hOpts.map((h) => <option key={h.id} value={h.id}>{h.name}</option>)}
          </select>
        </div>

        {/* Замена тренера */}
        <div className="field">
          <label style={{ display: 'flex', alignItems: 'center', gap: 8 }}>
            <input type="checkbox" checked={sub.on} onChange={(e) => setSub((s) => ({ ...s, on: e.target.checked }))} />
            Замена тренера
          </label>
          {sub.on && (
            <div className="stack" style={{ marginTop: 8 }}>
              <select className="input" value={sub.teacher_id} onChange={(e) => setSub((s) => ({ ...s, teacher_id: e.target.value }))}>
                <option value="">— кто заменяет —</option>
                {tOpts.map((t) => <option key={t.id} value={t.id}>{teacherName(t)}</option>)}
              </select>
              <input className="input" type="number" inputMode="numeric" placeholder="Сумма за замену, ₽"
                value={sub.amount} onChange={(e) => setSub((s) => ({ ...s, amount: e.target.value }))} />
              <input className="input" placeholder="Примечание (необязательно)"
                value={sub.note} onChange={(e) => setSub((s) => ({ ...s, note: e.target.value }))} />
            </div>
          )}
        </div>

        {err && <div className="badge danger" style={{ display: 'block', padding: '10px 12px', marginBottom: 12 }}>{err}</div>}

        <hr className="hr" />
        <div className="stack">
          <button className="btn btn-ghost" onClick={() => setMode('extend')} disabled={busy}>
            <Icon name="refresh" size={16} /> Продлить на недели
          </button>
          {item.status !== 'cancelled' && (
            <button className="btn btn-ghost" onClick={() => setMode('cancel')} disabled={busy}>
              <Icon name="x" size={16} /> Отменить занятие
            </button>
          )}
          <button className="btn btn-danger" onClick={() => setMode('delete')} disabled={busy}>
            <Icon name="trash" size={16} /> Удалить занятие
          </button>
        </div>
      </Sheet>
    );
  }
})();
