// СУ 47 — NEW FLOW VISION (draft): the order as a single editable batch ticket.
// No wizard, no side-rail stepper. Every field is visible and editable in any
// order, with a live total and one submit. Grade / district / volume-calculator
// open as bottom-sheets (which also makes the long district list reliably
// scrollable on touch). Reuses the existing helpers from tools.jsx:
//   gradeInfo, districts, submitQuote, phoneDigits, formatRuPhone, isValid*, VolumeHelper
//
// The legacy <QuoteFlow/> is still in tools.jsx; the Order section just points
// here now. Swap back by rendering <QuoteFlow/> again if this direction is wrong.

const { useState: useStateT, useEffect: useEffectT, useMemo: useMemoT, useRef: useRefT } = React;

// Local (not UTC) today — UTC slicing could hand back "yesterday" in UTC+3/+4
// and let a past date through. Used for the date floor + an onChange clamp.
function localTodayISO() {
  const d = new Date();
  return new Date(d.getTime() - d.getTimezoneOffset() * 60000).toISOString().slice(0, 10);
}

// Pump pricing is admin-editable (data.pump); these are only the fallback
// defaults if the store predates the field. Read live config via pumpConfig().
const PUMP_RATE_DEFAULT = 7000;          // ₽/ч
const PUMP_MIN_HOURS_DEFAULT = 5;        // minimum billed hours
function pumpConfig() {
  const p = (typeof SU47_DATA !== 'undefined' && SU47_DATA && SU47_DATA.pump) || {};
  const rate = Number(p.rate) > 0 ? Number(p.rate) : PUMP_RATE_DEFAULT;
  const minHours = Number(p.minHours) > 0 ? Number(p.minHours) : PUMP_MIN_HOURS_DEFAULT;
  return { rate, minHours, minCost: rate * minHours };
}

// Grade groups by price section (Товарный бетон / Цементный раствор). The two
// products reuse mark names (М100 …) at different prices, so grade selection
// carries the product too — otherwise a раствор М200 would price as concrete.
// Derived live from the admin-edited price list.
function priceGroups() {
  const out = []; let cur = null;
  const rows = (typeof SU47_DATA !== 'undefined' && SU47_DATA && SU47_DATA.prices) || [];
  for (const p of rows) {
    if (p.section) { cur = { label: p.section, grades: [] }; out.push(cur); continue; }
    if (!cur) { cur = { label: 'Марка', grades: [] }; out.push(cur); }
    if (p.grade) cur.grades.push({ grade: p.grade, price: Number(p.price) || 0, spec: p.spec || '', popular: !!p.popular });
  }
  return out.filter(g => g.grades.length);
}
function defaultProduct() { const g = priceGroups()[0]; return g ? g.label : null; }
// Price for a (product, grade) pair — falls back to the first group so a stale
// product label can't zero out a valid grade.
function gradePriceOf(product, grade) {
  if (!grade) return 0;
  const groups = priceGroups();
  const g = groups.find(x => x.label === product) || groups[0];
  const row = g && g.grades.find(r => r.grade === grade);
  return row ? row.price : 0;
}

/* ---- Bottom-sheet / modal (mobile: slides from bottom; desktop: centered) ---- */
function Sheet({ open, title, onClose, children }) {
  const panelRef = useRefT(null);
  useEffectT(() => {
    if (!open) return;
    const prevFocus = document.activeElement;
    document.body.style.overflow = 'hidden';
    const focusables = () => panelRef.current?.querySelectorAll(
      'a[href], button:not([disabled]), input, textarea, select, [tabindex]:not([tabindex="-1"])'
    ) || [];
    const onKey = (e) => {
      if (e.key === 'Escape') { onClose(); return; }
      if (e.key !== 'Tab') return;
      const f = focusables();
      if (!f.length) return;
      const first = f[0], last = f[f.length - 1];
      if (e.shiftKey && document.activeElement === first) { e.preventDefault(); last.focus(); }
      else if (!e.shiftKey && document.activeElement === last) { e.preventDefault(); first.focus(); }
    };
    document.addEventListener('keydown', onKey);
    panelRef.current?.focus();
    return () => {
      document.body.style.overflow = '';
      document.removeEventListener('keydown', onKey);
      if (prevFocus && prevFocus.focus) prevFocus.focus(); // restore focus to the trigger
    };
  }, [open]);

  // Match the sheet to the VISUAL viewport. The on-screen keyboard shrinks the
  // visual viewport but not the layout viewport, so a position:fixed sheet would
  // otherwise keep its full height and drop the list (and the focused input's
  // caret) behind the keyboard. Resizing to vv.height/offsetTop keeps the panel
  // and the caret inside the visible area on mobile.
  useEffectT(() => {
    if (!open) return;
    const vv = window.visualViewport;
    const sheetEl = panelRef.current?.parentElement;
    if (!vv || !sheetEl) return;
    // Coalesce updates into a single frame and react to RESIZE only (keyboard
    // open/close). Listening to vv 'scroll' made the sheet chase rubber-band
    // scrolls and jitter while typing — drop it.
    let raf = 0;
    const apply = () => {
      cancelAnimationFrame(raf);
      raf = requestAnimationFrame(() => {
        sheetEl.style.height = vv.height + 'px';
        sheetEl.style.top = vv.offsetTop + 'px';
      });
    };
    apply();
    vv.addEventListener('resize', apply);
    return () => { cancelAnimationFrame(raf); vv.removeEventListener('resize', apply); };
  }, [open]);
  if (!open) return null;
  // Portal to <body>: the ticket lives inside a `.reveal` wrapper whose
  // `will-change: transform` makes it a containing block for position:fixed,
  // which would otherwise pin the sheet mid-page instead of to the viewport.
  return ReactDOM.createPortal(
    <div className="sheet" role="dialog" aria-modal="true" aria-label={title}>
      <div className="sheet__scrim" onClick={onClose} />
      <div className="sheet__panel" ref={panelRef} tabIndex={-1}>
        <div className="sheet__head">
          <span className="sheet__title">{title}</span>
          <button type="button" className="sheet__close" aria-label="Закрыть" onClick={onClose}>✕</button>
        </div>
        <div className="sheet__body">{children}</div>
      </div>
    </div>,
    document.body
  );
}

/* ---- Searchable, scrollable district list (lives inside a Sheet) ---- */
function DistrictSheet({ open, value, onPick, onClose }) {
  const [q, setQ] = useStateT('');
  const inputRef = useRefT(null);
  useEffectT(() => {
    if (!open) return;
    setQ('');
    // Auto-focus only on desktop. On touch, focusing here would pop the keyboard
    // the instant the sheet opens — jumpy, and it hides half the list. Let the
    // user tap the search if they want to filter; the full list is right there.
    if (window.matchMedia('(pointer: fine)').matches) requestAnimationFrame(() => inputRef.current?.focus());
  }, [open]);
  const list = useMemoT(() => {
    const s = q.trim().toLowerCase();
    return SU47_DATA.districts.filter(d => !s || d.name.toLowerCase().includes(s));
  }, [q, open]);
  return (
    <Sheet open={open} title="Куда доставить?" onClose={onClose}>
      <div className="sheet__search">
        <input ref={inputRef} className="input" type="text" value={q}
          placeholder="Район, посёлок, город…" onChange={e => setQ(e.target.value)} />
      </div>
      <ul className="picklist" role="listbox" aria-label="Районы доставки">
        {list.length === 0 && <li className="picklist__empty">Ничего не найдено</li>}
        {list.map(d => (
          <li key={d.name}>
            <button type="button" role="option" aria-selected={d.name === value}
              className={`picklist__row ${d.name === value ? 'picklist__row--sel' : ''}`}
              onClick={() => onPick(d.name)}>
              <span className="picklist__name">{d.name}</span>
              <span className="picklist__price">+{d.price}&nbsp;₽/м³</span>
            </button>
          </li>
        ))}
      </ul>
    </Sheet>
  );
}

/* ---- Grade picker (grid of marks + a "подобрать" questionnaire from
   compareTree), lives inside a Sheet ---- */
function GradeSheet({ open, value, product, onPick, onClose }) {
  const groups = priceGroups();
  const tree = SU47_DATA.compareTree;
  const [mode, setMode] = useStateT('grid');   // 'grid' | 'quiz'
  const [gi, setGi] = useStateT(0);            // active product group
  const [cur, setCur] = useStateT(tree.start);
  const [result, setResult] = useStateT(null);
  useEffectT(() => {
    if (!open) return;
    setMode('grid');
    const idx = groups.findIndex(g => g.label === product);
    setGi(idx >= 0 ? idx : 0);
    setCur(tree.start); setResult(null);
  }, [open]);

  const group = groups[gi] || groups[0] || { label: '', grades: [] };
  const isConcrete = gi === 0;   // the подбор quiz (compareTree) is concrete-only

  const choose = (o) => {
    if (o.result) return setResult(o.result);
    if (o.next) {
      const s = tree.steps[o.next];
      if (s && !s.opts && s.result) setResult(s.result); // resolve result-only steps
      else setCur(o.next);
    }
  };
  const restart = () => { setCur(tree.start); setResult(null); };
  const step = tree.steps[cur];

  return (
    <Sheet open={open} title={mode === 'quiz' ? 'Подбор марки' : 'Марка'} onClose={onClose}>
      {mode === 'grid' && (
        <>
          {groups.length > 1 && (
            <div className="gradeseg" role="tablist" aria-label="Тип продукции">
              {groups.map((g, idx) => (
                <button key={g.label} type="button" role="tab" aria-selected={idx === gi}
                  className={`gradeseg__b ${idx === gi ? 'gradeseg__b--on' : ''}`}
                  onClick={() => setGi(idx)}>{g.label}</button>
              ))}
            </div>
          )}
          {isConcrete && (
            <button type="button" className="gradequiz-cta" onClick={() => { restart(); setMode('quiz'); }}>
              <span className="gradequiz-cta__txt">
                <b>Не знаю марку — подобрать</b>
                <small>Ответьте на пару вопросов о задаче</small>
              </span>
              <span className="gradequiz-cta__arrow" aria-hidden="true">→</span>
            </button>
          )}
          <div className="gradegrid">
            {group.grades.map(row => (
              <button key={row.grade} type="button"
                className={`gradegrid__item ${row.grade === value && group.label === product ? 'gradegrid__item--sel' : ''} ${row.popular ? 'gradegrid__item--pop' : ''}`}
                onClick={() => onPick(row.grade, group.label)}>
                <span className="gradegrid__g">{row.grade}</span>
                <span className="gradegrid__p">{row.price.toLocaleString('ru')}&nbsp;₽/м³</span>
                {row.popular && <span className="gradegrid__pop">★ популярный</span>}
              </button>
            ))}
          </div>
        </>
      )}

      {mode === 'quiz' && !result && step && step.opts && (
        <div className="gradequiz">
          <button type="button" className="gradequiz__back" onClick={() => setMode('grid')}>← все марки</button>
          <div className="gradequiz__q">{step.q}</div>
          {step.opts.map((o, i) => (
            <button key={i} type="button" className="gradequiz__opt" onClick={() => choose(o)}>{o.label}</button>
          ))}
        </div>
      )}

      {mode === 'quiz' && result && (() => {
        // Guard: a quiz leaf could reference a grade that was removed from the
        // price list. Server-side reconcile prunes such leaves, but never trust
        // the payload — a missing grade must not throw (that unmounts the page).
        const rg = SU47_DATA.gradeInfo[result];
        if (!rg) return (
          <div className="gradequiz__result">
            <span className="gradequiz__result-label">Не удалось подобрать марку</span>
            <div className="gradequiz__result-actions">
              <button type="button" className="btn btn--rust btn--block" onClick={restart}>Подобрать заново</button>
            </div>
          </div>
        );
        return (
          <div className="gradequiz__result">
            <span className="gradequiz__result-label">Рекомендуем для вашей задачи</span>
            <span className="gradequiz__result-g">{result} <small>· {rg.price.toLocaleString('ru')} ₽/м³</small></span>
            {rg.desc && <p className="gradequiz__result-desc">{rg.desc}</p>}
            <div className="gradequiz__result-actions">
              <button type="button" className="btn btn--rust btn--block" onClick={() => onPick(result, (groups[0] && groups[0].label) || product)}>Выбрать {result}</button>
              <button type="button" className="btn btn--ghost-dark btn--block" onClick={restart}>Подобрать заново</button>
            </div>
          </div>
        );
      })()}
    </Sheet>
  );
}

/* =====================================================
   QuoteTicket — the single editable order ticket.
   ===================================================== */
function QuoteTicket() {
  const today = localTodayISO();
  const [data, setData] = useStateT({
    grade: null,        // start empty — total stays 0 until the customer chooses
    product: defaultProduct(), // which price section the grade belongs to
    volume: 10,
    district: null,
    date: '',           // time is settled on the call, so the form omits it
    pump: false,
    name: '', phone: '', comment: '',
  });
  const [touched, setTouched] = useStateT({});
  const [submitting, setSubmitting] = useStateT(false);
  const [submitError, setSubmitError] = useStateT(null);
  const [done, setDone] = useStateT(false);
  const [sheet, setSheet] = useStateT(null); // 'grade' | 'district' | 'volume' | null
  const [flash, setFlash] = useStateT(null);  // 'grade' | 'volume' — brief field highlight

  const upd = (k, v) => setData(s => ({ ...s, [k]: v }));
  const touch = (k) => setTouched(t => ({ ...t, [k]: true }));
  // Visual acknowledgement when a field is set indirectly (price-table click,
  // volume calculator) — the same gesture for both, so it reads as one system.
  const pulse = (key) => { setFlash(key); setTimeout(() => setFlash(f => (f === key ? null : f)), 1100); };

  // Deep-link from the price table → preselect the grade (with its product, so
  // раствор prices as раствор) + flash the Марка field.
  useEffectT(() => {
    const onSel = (e) => {
      const g = e.detail?.grade;
      if (!g) return;
      const prod = e.detail?.product || defaultProduct();
      if (gradePriceOf(prod, g) > 0 || SU47_DATA.gradeInfo[g]) {
        setData(s => ({ ...s, grade: g, product: prod })); pulse('grade');
      }
    };
    window.addEventListener('su47:selectGrade', onSel);
    return () => window.removeEventListener('su47:selectGrade', onSel);
  }, []);

  // On successful submit, bring the «Принято» card into view (the form collapses
  // to a shorter success state, so the page must scroll up to it).
  useEffectT(() => {
    if (!done) return;
    const el = document.getElementById('order');
    if (!el) return;
    const reduce = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    el.scrollIntoView({ behavior: reduce ? 'auto' : 'smooth', block: 'start' });
  }, [done]);

  const pump = pumpConfig();
  const gradePrice = gradePriceOf(data.product, data.grade);
  const deliveryPer = data.district ? (SU47_DATA.districts.find(d => d.name === data.district)?.price || 0) : 0;
  const vol = Math.max(0, Number(data.volume) || 0);
  const total = (gradePrice + deliveryPer) * vol + (data.pump ? pump.minCost : 0);

  const errors = {
    name: isValidName(data.name) ? null : 'Укажите имя (минимум 2 символа)',
    phone: isValidPhone(data.phone) ? null : 'Введите номер полностью',
  };
  const contactValid = !errors.name && !errors.phone;

  const setDate = (v) => upd('date', v && v < today ? today : v);
  const stepVolume = (delta) => upd('volume', Math.max(1, (Number(data.volume) || 0) + delta));

  const submit = async () => {
    setTouched({ name: true, phone: true });
    if (!contactValid) return;
    setSubmitting(true); setSubmitError(null);
    const res = await submitQuote({
      ...data, phone: '+' + phoneDigits(data.phone), total, createdAt: new Date().toISOString(),
    });
    setSubmitting(false);
    if (res.ok) setDone(true);
    else setSubmitError('Не удалось отправить заявку. Попробуйте ещё раз или позвоните нам напрямую.');
  };

  const reset = () => {
    setDone(false); setTouched({}); setSubmitError(null);
    setData({ grade: null, product: defaultProduct(), volume: 10, district: null, date: '', pump: false, name: '', phone: '', comment: '' });
  };

  if (done) {
    return (
      <div className="ticket ticket--done" data-scroll="quote">
        <div className="ticket__stamp">Принято</div>
        <h2 className="ticket__done-title">Заявка отправлена</h2>
        <p className="ticket__done-text">
          Получили ваш расчёт на&nbsp;<strong>{total.toLocaleString('ru')}&nbsp;₽</strong>. Менеджер перезвонит
          на&nbsp;{data.phone || 'указанный номер'} в&nbsp;течение 15&nbsp;минут — подтвердит детали и&nbsp;время доставки.
        </p>
        <button type="button" className="btn btn--ghost-dark" onClick={reset}>Новая заявка</button>
      </div>
    );
  }

  return (
    <div className="order2" data-scroll="quote">
      <div className="order2__intro">
        <span className="order2__eyebrow">Расчёт&nbsp;и&nbsp;заявка</span>
        <h2 className="order2__title">Соберите заявку</h2>
        <p className="order2__sub">Марка, объём, доставка и&nbsp;дата — в&nbsp;одном расчёте. Стоимость с&nbsp;доставкой видна сразу, заявку подтвердит менеджер.</p>
        <ul className="order2__assure">
          <li>Перезвоним в&nbsp;течение 15&nbsp;минут</li>
          <li>Цена сразу с&nbsp;доставкой</li>
          <li>Предварительный расчёт — ни&nbsp;к&nbsp;чему не&nbsp;обязывает</li>
        </ul>
      </div>

      <div className="ticket">
        <div className="ticket__head">
          <span className="ticket__head-k">Заявка&nbsp;· СУ&nbsp;47</span>
        </div>

        {/* МАРКА — select-style control */}
        <button type="button" className={`trow trow--btn ${data.grade ? '' : 'trow--empty'} ${flash === 'grade' ? 'trow--flash' : ''}`} onClick={() => setSheet('grade')}>
          <span className="trow__k">Марка</span>
          <span className="trow__field">
            <span className="trow__v">{data.grade ? `${data.grade}${data.product ? ' · ' + data.product.split(' ').pop().toLowerCase() : ''}` : 'Выберите марку'}</span>
            {data.grade && <span className="trow__hint">{gradePrice.toLocaleString('ru')}&nbsp;₽/м³</span>}
            <span className="trow__chev" aria-hidden="true">▾</span>
          </span>
        </button>

        {/* ОБЪЁМ */}
        <div className={`trow trow--volume ${flash === 'volume' ? 'trow--flash' : ''}`}>
          <span className="trow__k">Объём, м³</span>
          <div className="trow__volume">
            <div className="stepper">
              <button type="button" className="stepper__b" aria-label="Меньше" onClick={() => stepVolume(-1)}>−</button>
              <input className="stepper__in" type="number" inputMode="decimal" min="1" value={data.volume}
                onChange={e => upd('volume', e.target.value === '' ? '' : Number(e.target.value))}
                onBlur={e => { if (!e.target.value || Number(e.target.value) < 1) upd('volume', 1); }} />
              <span className="stepper__u">м³</span>
              <button type="button" className="stepper__b" aria-label="Больше" onClick={() => stepVolume(1)}>+</button>
            </div>
            <button type="button" className="trow__calc" onClick={() => setSheet('volume')}>
              <span className="trow__calc-ic" aria-hidden="true">⊞</span> Не&nbsp;знаю — посчитать
            </button>
          </div>
        </div>

        {/* ДОСТАВКА — select-style control */}
        <button type="button" className={`trow trow--btn ${data.district ? '' : 'trow--empty'}`} onClick={() => setSheet('district')}>
          <span className="trow__k">Доставка</span>
          <span className="trow__field">
            <span className="trow__v">{data.district || 'Выберите район'}</span>
            {data.district && <span className="trow__hint">+{deliveryPer.toLocaleString('ru')}&nbsp;₽/м³</span>}
            <span className="trow__chev" aria-hidden="true">▾</span>
          </span>
        </button>

        {/* ДАТА — time is settled on the call, so the form asks only for the day */}
        <div className="trow">
          <span className="trow__k">Дата доставки</span>
          <div className="trow__datetime">
            <input id="t-date" className="input input--date" type="date" min={today}
              value={data.date} onChange={e => setDate(e.target.value)} aria-label="Дата доставки" />
          </div>
        </div>

        {/* НАСОС */}
        <label className="trow trow--toggle">
          <span className="trow__toggle-label">
            <span className="trow__toggle-title">Бетононасос</span>
            <span className="trow__toggle-txt">+{pump.rate.toLocaleString('ru')}&nbsp;₽/ч&nbsp;· мин. {pump.minHours}&nbsp;ч</span>
          </span>
          <input type="checkbox" className="trow__check" checked={data.pump} onChange={e => upd('pump', e.target.checked)} />
          <span className="trow__switch" aria-hidden="true" />
        </label>

        {/* CONTACTS */}
        <div className="ticket__contacts">
          <div className="ticket__grid2">
            <div className="field">
              <label className="label" htmlFor="t-name">Имя <span className="field__req">*</span></label>
              <input id="t-name" className={`input ${touched.name && errors.name ? 'input--error' : ''}`}
                value={data.name} onChange={e => upd('name', e.target.value)} onBlur={() => touch('name')}
                aria-invalid={!!(touched.name && errors.name)} placeholder="Как к вам обращаться" />
              {touched.name && errors.name && <span className="field__error">{errors.name}</span>}
            </div>
            <div className="field">
              <label className="label" htmlFor="t-phone">Телефон <span className="field__req">*</span></label>
              <input id="t-phone" className={`input ${touched.phone && errors.phone ? 'input--error' : ''}`}
                type="tel" inputMode="tel" value={data.phone}
                onChange={e => upd('phone', formatRuPhone(e.target.value))} onBlur={() => touch('phone')}
                aria-invalid={!!(touched.phone && errors.phone)} placeholder="+7 (___) ___-__-__" />
              {touched.phone && errors.phone && <span className="field__error">{errors.phone}</span>}
            </div>
          </div>
          <div className="field">
            <label className="label" htmlFor="t-comment">Комментарий <span className="field__opt">— по желанию</span></label>
            <textarea id="t-comment" className="textarea" rows="2" value={data.comment}
              onChange={e => upd('comment', e.target.value)} placeholder="Подъезд, особенности объекта" />
          </div>
        </div>

        {/* TOTAL + SUBMIT */}
        <div className="ticket__foot">
          <div className="ticket__total">
            <span className="ticket__total-k">{(data.grade && data.district) ? 'Итого предварительно' : 'Выберите марку и район — посчитаем цену'}</span>
            <span className="ticket__total-v" aria-live="polite">{total.toLocaleString('ru')}&nbsp;₽</span>
          </div>
          {submitError && <div className="ticket__error" role="alert">{submitError}</div>}
          {Object.values(touched).some(Boolean) && !contactValid &&
            <div className="ticket__hint">Заполните имя и&nbsp;телефон, чтобы отправить.</div>}
          <button type="button" className="btn btn--rust btn--lg btn--block" disabled={submitting} onClick={submit}>
            {submitting ? 'Отправляем…' : <>Отправить заявку <span className="arrow">→</span></>}
          </button>
          <p className="ticket__consent">Нажимая «Отправить», вы&nbsp;соглашаетесь с&nbsp;<a href="/privacy.html#policy">политикой обработки персональных данных</a>.</p>
        </div>
      </div>

      <GradeSheet open={sheet === 'grade'} value={data.grade} product={data.product}
        onPick={(g, prod) => { setData(s => ({ ...s, grade: g, product: prod || s.product })); setSheet(null); }} onClose={() => setSheet(null)} />
      <DistrictSheet open={sheet === 'district'} value={data.district}
        onPick={d => { upd('district', d); setSheet(null); }} onClose={() => setSheet(null)} />
      <Sheet open={sheet === 'volume'} title="Посчитать по размерам" onClose={() => setSheet(null)}>
        <VolumeHelper onApply={(v) => { upd('volume', v); setSheet(null); pulse('volume'); }} />
      </Sheet>
    </div>
  );
}

Object.assign(window, { QuoteTicket, Sheet, DistrictSheet, GradeSheet });
