/* measurements-app.jsx — wizard замеров (7 шагов) */
(function () {
  const { useState, useEffect, useMemo, useCallback, useRef } = React;
  const STEPS = [
    { id: 'rooms', label: 'Помещения' },
    { id: 'walls', label: 'Стены' },
    { id: 'openings', label: 'Проёмы' },
    { id: 'construct', label: 'Конструктив' },
    { id: 'engineering', label: 'Инженерия' },
    { id: 'media', label: 'Комментарии' },
    { id: 'summary', label: 'Итог' },
  ];

  const DEMO = window.MeasureDemo;
  const CALC = window.MeasureCalc;
  const LS_KEY = DEMO.LS_KEY;

  function loadSession(embedded) {
    if (embedded && DEMO.createTemplatePreviewSession) return DEMO.createTemplatePreviewSession();
    try {
      const raw = sessionStorage.getItem(LS_KEY);
      if (raw) return JSON.parse(raw);
    } catch (e) { /* ignore */ }
    return DEMO.createSession();
  }

  function fmtTime(iso) {
    if (!iso) return '—';
    try {
      return new Date(iso).toLocaleTimeString('ru-RU', { hour: '2-digit', minute: '2-digit' });
    } catch (e) { return '—'; }
  }

  function Field({ label, children, hint }) {
    return (
      <div className="fe-field">
        {label && <label className="fe-label">{label}</label>}
        {children}
        {hint && <span style={{ fontSize: 11, color: 'var(--text-tertiary)' }}>{hint}</span>}
      </div>
    );
  }

  function ReadonlyHeader({ obj, compact }) {
    const pt = (DEMO.PROPERTY_TYPES.find(function (p) { return p.id === obj.propertyType; }) || {}).label || obj.propertyType;
    if (compact) {
      return (
        <div className="fe-readonly ms-readonly-compact">
          <div className="ms-ro-main">
            <div className="fe-ro-val">{obj.address}</div>
            <div className="ms-ro-meta">{obj.dealId} · {obj.clientName} · {pt}</div>
          </div>
        </div>
      );
    }
    return (
      <div className="fe-readonly">
        <div><div className="fe-ro-label">Адрес</div><div className="fe-ro-val">{obj.address}</div></div>
        <div><div className="fe-ro-label">Тип</div><div className="fe-ro-val">{pt}</div></div>
        <div><div className="fe-ro-label">Клиент</div><div className="fe-ro-val">{obj.clientName}</div></div>
        <div><div className="fe-ro-label">Сделка</div><div className="fe-ro-val">{obj.dealId}</div></div>
      </div>
    );
  }

  function CtorBtn({ variant, children, disabled, onClick }) {
    const cls = 'ms-btn ' + (variant === 'secondary' ? 'ms-btn-secondary' : 'ms-btn-primary');
    return <button type="button" className={cls} disabled={disabled} onClick={onClick}>{children}</button>;
  }

  function resolveBtn(embedded) {
    const DS = window.DesignSystem_9e0a09;
    if (embedded || !DS) return CtorBtn;
    return DS.Button;
  }

  function msModeLabel(room) {
    return { rectangular: 'Прямоуг.', l_shaped: 'Г-образное', freeform: 'Свободная' }[room.geometryMode] || room.geometryMode;
  }

  function msTypeLabel(room) {
    const map = { bathroom: 'Санузел', shower: 'Ванная', balcony: 'Балкон', loggia: 'Лоджия', terrace: 'Терраса', outdoor_plot: 'Участок' };
    return map[room.roomType] || null;
  }

  function msTemplateHint(tpl) {
    if (tpl.geometryMode === 'rectangular') return 'L×W×H';
    if (tpl.geometryMode === 'l_shaped') return 'Г-контур';
    if (tpl.geometryMode === 'freeform') return 'контур';
    return '';
  }

  const MS_TEMPLATE_ROW_SIZE = 3;

  function msChunkByRow(items, maxPerRow) {
    const limit = maxPerRow || MS_TEMPLATE_ROW_SIZE;
    const rows = [];
    for (let i = 0; i < items.length; i += limit) {
      rows.push(items.slice(i, i + limit));
    }
    return rows;
  }

  function msCardRowSpanClass(countInRow) {
    if (countInRow === 1) return 'ms-card-row-span-6';
    if (countInRow === 2) return 'ms-card-row-span-3';
    return 'ms-card-row-span-2';
  }

  function MsGeoScheme({ compact }) {
    return (
      <div className={'ms-geo-scheme' + (compact ? ' ms-geo-scheme--compact' : '')}>
        <svg viewBox="0 0 120 100" aria-hidden="true">
          <rect x="20" y="15" width="80" height="70" fill="none" stroke="currentColor" strokeWidth="1.5" />
          <path d="M60 85 L60 95 L45 95" fill="none" stroke="var(--primary)" strokeWidth="2" />
          <text x="8" y="55" fontSize="8" fill="currentColor">1</text>
          <text x="58" y="12" fontSize="8" fill="currentColor">2</text>
          <text x="105" y="55" fontSize="8" fill="currentColor">3</text>
          <text x="58" y="78" fontSize="8" fill="currentColor">4</text>
          <text x="52" y="99" fontSize="7" fill="var(--primary)">вход</text>
        </svg>
        {!compact && <div>Вход снизу · обход clockwise</div>}
      </div>
    );
  }

  function MsDimGrid({ room, outdoor, showTerraceHeight, onPatch }) {
    const vol = CALC.calcRoomVolumes(room);
    const L = +room.lengthM || 0;
    const W = +room.widthM || 0;
    const showHeight = !outdoor || showTerraceHeight;
    const areaText = L > 0 && W > 0 ? vol.floor : (+room.areaFloorM2 > 0 ? +room.areaFloorM2 : null);

    function patchRect(p) {
      const next = Object.assign({}, p, { wallsConfirmed: false });
      if (p.lengthM !== undefined || p.widthM !== undefined) {
        next.walls = CALC.genWallsRect(Object.assign({}, room, next));
      }
      const probe = Object.assign({}, room, next);
      if (CALC.wallsMatchRect(probe)) {
        next.wallsReviewMode = 'summary';
      }
      onPatch(next);
    }

    function patchHeight(heightM) {
      const H = heightM === null ? null : +heightM;
      const next = {
        heightM: H,
        walls: CALC.syncWallsHeights(room, H),
        wallsConfirmed: false,
      };
      const probe = Object.assign({}, room, next);
      if (CALC.wallsMatchRect(probe)) {
        next.wallsReviewMode = 'summary';
      }
      onPatch(next);
    }

    return (
      <div className={'ms-dim-grid' + (showHeight ? '' : ' ms-dim-grid--no-height')}>
        <div className="ms-dim-cell">
          <label className="ms-dim-cell-label" htmlFor={'dim-l-' + room.id}>Длина, м</label>
          <input id={'dim-l-' + room.id} className="fe-input" type="number" step="0.05" value={room.lengthM || ''} onChange={(e) => patchRect({ lengthM: +e.target.value })} />
        </div>
        <div className="ms-dim-cell">
          <label className="ms-dim-cell-label" htmlFor={'dim-w-' + room.id}>Ширина, м</label>
          <input id={'dim-w-' + room.id} className="fe-input" type="number" step="0.05" value={room.widthM || ''} onChange={(e) => patchRect({ widthM: +e.target.value })} />
        </div>
        {showHeight && (
          <div className="ms-dim-cell">
            <label className="ms-dim-cell-label" htmlFor={'dim-h-' + room.id}>{showTerraceHeight ? 'Ограждение, м' : 'Высота, м'}</label>
            <input id={'dim-h-' + room.id} className="fe-input" type="number" step="0.01" value={room.heightM || ''} onChange={(e) => {
              const raw = e.target.value;
              patchHeight(showTerraceHeight && !raw ? null : +raw);
            }} placeholder={showTerraceHeight ? '—' : undefined} />
          </div>
        )}
        <div className="ms-dim-cell ms-dim-cell--readonly">
          <span className="ms-dim-cell-label">Площадь</span>
          <div className="ms-dim-readout">{areaText != null ? areaText + ' м²' : '—'}</div>
        </div>
        <details className="ms-dim-more">
          <summary>Override площади</summary>
          <div className="ms-dim-more-body">
            <label className="ms-dim-cell-label" htmlFor={'dim-area-' + room.id}>Площадь, м²</label>
            <input id={'dim-area-' + room.id} className="fe-input" type="number" step="0.1" value={room.areaFloorM2 || ''} onChange={(e) => onPatch({ areaFloorM2: e.target.value ? +e.target.value : null })} placeholder="опционально" />
          </div>
        </details>
      </div>
    );
  }

  function MsRoomCardBody({ room, onPatch, onGoWalls, embedded }) {
    const outdoor = CALC.isOutdoorRoom(room);
    const mode = room.geometryMode || 'rectangular';
    const showRectInline = mode === 'rectangular';
    const showOutdoorArea = outdoor && mode === 'freeform';
    const showTerraceRect = room.roomType === 'terrace' && mode === 'rectangular';

    if (showRectInline) {
      return (
        <div className="ms-room-card-body">
          <MsDimGrid
            room={room}
            outdoor={outdoor}
            showTerraceHeight={showTerraceRect}
            onPatch={onPatch}
          />
        </div>
      );
    }

    if (showOutdoorArea) {
      return (
        <div className="ms-room-card-body">
          <Field label="Площадь, м²" hint={room.roomType === 'outdoor_plot' ? 'Контур участка можно уточнить на шаге «Стены»' : 'Или задайте контур на шаге «Стены»'}>
            <input className="fe-input" type="number" step="0.1" value={room.areaFloorM2 || ''} onChange={(e) => onPatch({ areaFloorM2: e.target.value ? +e.target.value : null })} />
          </Field>
          {!embedded && onGoWalls && (
            <button type="button" className="ms-room-card-link" onClick={onGoWalls}>Уточнить периметр на шаге «Стены» →</button>
          )}
        </div>
      );
    }

    if (mode === 'l_shaped') {
      const segs = room.lSegments || [];
      const walls = CALC.ensureWalls(room);
      const filled = walls.filter(function (w) { return +w.lengthM > 0; }).length;
      return (
        <div className="ms-room-card-body ms-room-card-body--deferred">
          <p>{segs.length} сегментов · заполнено {filled}/{walls.length}</p>
          {!outdoor && (
            <Field label="Высота, м"><input className="fe-input" type="number" step="0.01" value={room.heightM || ''} onChange={(e) => onPatch({ heightM: +e.target.value })} /></Field>
          )}
          {!embedded && onGoWalls ? (
            <button type="button" className="ms-room-card-link" onClick={onGoWalls}>Заполнить на шаге «Стены» →</button>
          ) : <p className="ms-room-card-hint">Сегменты стен — на шаге «Стены»</p>}
        </div>
      );
    }

    if (mode === 'freeform') {
      const wallN = (room.walls || []).filter(function (w) { return +w.lengthM > 0; }).length;
      return (
        <div className="ms-room-card-body ms-room-card-body--deferred">
          <Field label="Площадь пола, м²">
            <input className="fe-input" type="number" step="0.1" value={room.areaFloorM2 || ''} onChange={(e) => onPatch({ areaFloorM2: e.target.value ? +e.target.value : null })} />
          </Field>
          <p>Стен с длиной: {wallN}</p>
          {!outdoor && (
            <Field label="Высота, м"><input className="fe-input" type="number" step="0.01" value={room.heightM || ''} onChange={(e) => onPatch({ heightM: +e.target.value })} /></Field>
          )}
          {!embedded && onGoWalls ? (
            <button type="button" className="ms-room-card-link" onClick={onGoWalls}>Длины стен — на шаге «Стены» →</button>
          ) : <p className="ms-room-card-hint">Длины стен — на шаге «Стены»</p>}
        </div>
      );
    }

    return null;
  }

  function MsRoomCard({ variant, room, index, active, onSelect, onPatch, onDelete, onGoWalls, embedded }) {
    const vol = CALC.calcRoomVolumes(room);
    const complete = CALC.roomGeometryComplete(room);
    const typeLbl = msTypeLabel(room);

    if (variant === 'compact') {
      return (
        <button
          type="button"
          className={'ms-room-card ms-room-card--compact' + (active ? ' is-active' : '') + (complete ? ' is-done' : '')}
          onClick={() => onSelect(room.id)}
          title={room.name}
        >
          <span className="ms-room-card-compact-name">{room.name}</span>
          {complete ? (
            <svg className="ms-room-card-compact-check" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true"><polyline points="20 6 9 17 4 12" /></svg>
          ) : (
            <span className="ms-room-card-compact-dot" aria-hidden="true" />
          )}
        </button>
      );
    }

    return (
      <article className={'ms-room-card' + (complete ? ' is-done' : '')}>
        <div className="ms-room-card-head">
          <span className="ms-room-card-index">{index + 1}</span>
          <div className="ms-room-card-meta-block">
            <input className="fe-input ms-room-card-name" value={room.name} onChange={(e) => onPatch({ name: e.target.value })} aria-label="Название помещения" />
            <div className="ms-room-card-sub">
              {msModeLabel(room)}{typeLbl ? ' · ' + typeLbl : ''}{room.zoneKind === 'outdoor' ? ' · открытая зона' : ''}
              {' · '}{vol.floor > 0 ? vol.floor + ' м²' : 'без площади'}
            </div>
          </div>
          <button type="button" className="ms-del" onClick={onDelete} title="Удалить"><Ic n="trash-2" s={14} /></button>
        </div>
        <MsRoomCardBody room={room} onPatch={onPatch} onGoWalls={onGoWalls} embedded={embedded} />
        {complete && (
          <div className="ms-room-card-foot">
            {'Пол ' + vol.floor + ' м² · периметр ' + vol.perimeter + ' м'}
          </div>
        )}
      </article>
    );
  }

  function MsRoomStrip({ rooms, activeId, onSelect }) {
    if (!rooms || rooms.length <= 1) return null;
    return (
      <div className="ms-room-strip" role="tablist" aria-label="Помещения">
        {rooms.map(function (r) {
          return (
            <MsRoomCard
              key={r.id}
              variant="compact"
              room={r}
              active={r.id === activeId}
              onSelect={onSelect}
            />
          );
        })}
      </div>
    );
  }

  function RoomTemplateAdder({ onAdd }) {
    const tplById = {};
    DEMO.ROOM_TEMPLATES.forEach(function (t) { tplById[t.id] = t; });
    const groups = DEMO.ROOM_TEMPLATE_GROUPS || [{ id: 'all', label: 'Шаблоны', templateIds: DEMO.ROOM_TEMPLATES.map(function (t) { return t.id; }) }];
    return (
      <div className="ms-zone-picker" role="presentation">
        {groups.map(function (g) {
          const rows = msChunkByRow(g.templateIds, MS_TEMPLATE_ROW_SIZE);
          return (
            <section key={g.id} className="ms-zone-picker-col" aria-labelledby={'zone-col-' + g.id}>
              <h3 className="ms-zone-picker-col-title" id={'zone-col-' + g.id}>{g.label}</h3>
              <div className="ms-template-card-rows">
                {rows.map(function (rowIds, rowIdx) {
                  return (
                    <div
                      key={g.id + '-row-' + rowIdx}
                      className="ms-template-card-row"
                    >
                      {rowIds.map(function (tid) {
                        const t = tplById[tid];
                        if (!t) return null;
                        const hint = msTemplateHint(t);
                        const spanClass = msCardRowSpanClass(rowIds.length);
                        return (
                          <button key={t.id} type="button" className={'ms-template-card ' + spanClass} onClick={() => onAdd(t)}>
                            <span className="ms-template-card-label">{t.label}</span>
                            {hint ? <span className="ms-template-card-hint">{hint}</span> : null}
                          </button>
                        );
                      })}
                    </div>
                  );
                })}
              </div>
            </section>
          );
        })}
      </div>
    );
  }

  function StepRooms({ obj, patchObj, onGoWalls, embedded }) {
    const rooms = obj.rooms || [];
    const filled = rooms.filter(function (r) { return CALC.roomGeometryComplete(r); }).length;
    const total = rooms.length;

    function addFromTemplate(tpl) {
      const n = rooms.length + 1;
      const defaultName = {
        bathroom: 'Санузел',
        shower: 'Ванная',
        balcony: 'Балкон',
        loggia: 'Лоджия',
        terrace: 'Терраса',
        outdoor_plot: 'Участок',
      }[tpl.roomType] || ('Помещение ' + n);
      const room = DEMO.mkRoom(defaultName, {
        geometryMode: tpl.geometryMode,
        templateId: tpl.id,
        roomType: tpl.roomType || 'other',
        zoneKind: tpl.zoneKind || 'interior',
        sortOrder: n,
        lSegments: tpl.geometryMode === 'l_shaped' ? [3, 2, 1.5, 2, 1.5, 3] : undefined,
      });
      patchObj({ rooms: rooms.concat([room]) });
    }

    function patchRoom(id, p) {
      patchObj({ rooms: rooms.map(function (r) { return r.id === id ? Object.assign({}, r, p) : r; }) });
    }

    function delRoom(id) {
      const r = rooms.find(function (x) { return x.id === id; });
      const empty = !CALC.roomGeometryComplete(r);
      if (!empty && !window.confirm('Удалить «' + r.name + '»?')) return;
      patchObj({ rooms: rooms.filter(function (x) { return x.id !== id; }) });
    }

    return (
      <React.Fragment>
        <div className="ms-progress-pill">{filled}/{total} помещений с геометрией</div>
        <div className="fe-section ms-zone-picker-section">
          <h2 className="fe-section-title ms-zone-picker-heading" id="ms-zone-picker-heading">Добавить зону</h2>
          <RoomTemplateAdder onAdd={addFromTemplate} />
        </div>
        {rooms.length === 0 && <div className="fe-empty" style={{ padding: 24 }}>Добавьте первое помещение или открытую зону</div>}
        <div className="ms-room-card-rows">
          {msChunkByRow(rooms, MS_TEMPLATE_ROW_SIZE).map(function (rowRooms, rowIdx) {
            const spanClass = msCardRowSpanClass(rowRooms.length);
            const baseIdx = rowIdx * MS_TEMPLATE_ROW_SIZE;
            return (
              <div key={'room-row-' + rowIdx} className="ms-room-card-row">
                {rowRooms.map(function (room, i) {
                  const idx = baseIdx + i;
                  return (
                    <div key={room.id} className={spanClass}>
                      <MsRoomCard
                        variant="full"
                        room={room}
                        index={idx}
                        onPatch={function (p) { patchRoom(room.id, p); }}
                        onDelete={function () { delRoom(room.id); }}
                        onGoWalls={onGoWalls ? function () { onGoWalls(room.id); } : null}
                        embedded={embedded}
                      />
                    </div>
                  );
                })}
              </div>
            );
          })}
        </div>
      </React.Fragment>
    );
  }

  function StepWalls({ room, patchRoom }) {
    if (!room) return <div className="fe-empty">Сначала добавьте помещение</div>;
    const walls = CALC.ensureWalls(room);
    const isRect = (room.geometryMode || 'rectangular') === 'rectangular';
    const reviewMode = room.wallsReviewMode || (isRect ? 'summary' : 'detail');
    const showSummary = isRect && reviewMode === 'summary';
    const canCollapse = isRect && CALC.wallsMatchRect(room);
    const vol = CALC.calcRoomVolumes(room);
    const roomH = +room.heightM || 0;

    function patchWall(wid, p) {
      const nextWalls = walls.map(function (w) { return w.id === wid ? Object.assign({}, w, p) : w; });
      const patch = { walls: nextWalls, wallsConfirmed: false };
      const probe = Object.assign({}, room, patch);
      if (isRect && !CALC.wallsMatchRect(probe)) {
        patch.wallsReviewMode = 'detail';
      }
      patchRoom(patch);
    }

    function addWall() {
      const H = room.heightM || 2.75;
      const idx = walls.length + 1;
      patchRoom({ walls: walls.concat([{ id: CALC.uid(), index: idx, label: 'Стена ' + idx, lengthM: 0, heightM: H }]), wallsReviewMode: 'detail', wallsConfirmed: false });
    }

    return (
      <React.Fragment>
        {!showSummary && (
          <React.Fragment>
            <div className="ms-hint">
              <Ic n="info" s={16} c="var(--primary)" />
              <span>Обходите помещение <strong>по часовой стрелке</strong>, начиная <strong>слева от входа</strong>. Стена 1 — слева от входа.</span>
            </div>
            <MsGeoScheme compact={false} />
          </React.Fragment>
        )}
        {room.geometryMode === 'freeform' && (
          <Field label="Площадь пола, м² (manual)">
            <input className="fe-input" type="number" step="0.1" value={room.areaFloorM2 || ''} onChange={(e) => patchRoom({ areaFloorM2: +e.target.value })} />
          </Field>
        )}
        <div className="fe-section">
          <h2 className="fe-section-title">Стены · {room.name}</h2>
          {showSummary ? (
            <div className={'ms-walls-summary' + (room.wallsConfirmed ? ' is-confirmed' : '')}>
              <p className="ms-walls-summary-lead">Сгенерировано из размеров на шаге «Помещения»</p>
              <p className="ms-walls-summary-stats">
                Пол {vol.floor} м² · периметр {vol.perimeter} м{roomH > 0 ? (' · высота ' + roomH + ' м') : ''}
              </p>
              <MsGeoScheme compact={true} />
              <table className="ms-walls-summary-table">
                <thead>
                  <tr>
                    <th scope="col">Стена</th>
                    <th scope="col">Длина</th>
                    <th scope="col">Высота</th>
                  </tr>
                </thead>
                <tbody>
                  {walls.map(function (w) {
                    const h = w.heightM || room.heightM || '';
                    return (
                      <tr key={w.id}>
                        <td>{w.label || ('Стена ' + w.index)}</td>
                        <td>{w.lengthM || '—'} м</td>
                        <td>{h ? h + ' м' : '—'}</td>
                      </tr>
                    );
                  })}
                </tbody>
              </table>
              <div className="ms-walls-summary-actions">
                <button type="button" className="ms-btn ms-btn-primary" onClick={() => patchRoom({ wallsConfirmed: true })}>
                  {room.wallsConfirmed ? 'Подтверждено' : 'Всё верно'}
                </button>
                <button type="button" className="ms-room-card-link" onClick={() => patchRoom({ wallsReviewMode: 'detail', wallsConfirmed: false })}>
                  Править по стенам
                </button>
              </div>
            </div>
          ) : (
            <React.Fragment>
              {canCollapse && (
                <button type="button" className="ms-room-card-link ms-walls-collapse" onClick={() => patchRoom({ wallsReviewMode: 'summary' })}>
                  ← Свернуть в сводку
                </button>
              )}
              {walls.map(function (w) {
                return (
                  <div key={w.id} className="ms-wall-row" style={{ marginBottom: 8 }}>
                    <Field label={w.label || ('Стена ' + w.index)}>
                      <input className="fe-input" type="number" step="0.05" value={w.lengthM || ''} onChange={(e) => patchWall(w.id, { lengthM: +e.target.value })} placeholder="длина" />
                    </Field>
                    <Field label="Выс., м">
                      <input className="fe-input" type="number" step="0.01" value={w.heightM || room.heightM || ''} onChange={(e) => patchWall(w.id, { heightM: +e.target.value })} />
                    </Field>
                    <Field label="Коммент.">
                      <input className="fe-input" value={w.comment || ''} onChange={(e) => patchWall(w.id, { comment: e.target.value })} />
                    </Field>
                    {room.geometryMode === 'freeform' && (
                      <button type="button" className="ms-del ms-del" onClick={() => patchRoom({ walls: walls.filter(function (x) { return x.id !== w.id; }) })}><Ic n="x" s={14} /></button>
                    )}
                  </div>
                );
              })}
              {room.geometryMode === 'freeform' && (
                <button type="button" className="fe-add-room ms-add" onClick={addWall}>+ Добавить стену</button>
              )}
            </React.Fragment>
          )}
        </div>
        <details className="fe-section">
          <summary className="fe-section-title" style={{ cursor: 'pointer' }}>Нестандартная геометрия / старый фонд</summary>
          <div style={{ marginTop: 12 }}>
            <label className="fe-check"><input type="checkbox" checked={!!room.geometryExtras?.isOldBuilding} onChange={(e) => patchRoom({ geometryExtras: Object.assign({}, room.geometryExtras, { isOldBuilding: e.target.checked }) })} /><span>Старый фонд</span></label>
            <div className="fe-grid2" style={{ marginTop: 10 }}>
              <Field label="Диагональ 1, м"><input className="fe-input" type="number" step="0.01" value={(room.geometryExtras?.diagonalsM || [])[0] || ''} onChange={(e) => {
                const d = (room.geometryExtras?.diagonalsM || []).slice();
                d[0] = +e.target.value;
                patchRoom({ geometryExtras: Object.assign({}, room.geometryExtras, { diagonalsM: d }) });
              }} /></Field>
              <Field label="Диагональ 2, м"><input className="fe-input" type="number" step="0.01" value={(room.geometryExtras?.diagonalsM || [])[1] || ''} onChange={(e) => {
                const d = (room.geometryExtras?.diagonalsM || []).slice();
                d[1] = +e.target.value;
                patchRoom({ geometryExtras: Object.assign({}, room.geometryExtras, { diagonalsM: d }) });
              }} /></Field>
            </div>
          </div>
        </details>
        {(() => {
          const v = CALC.calcRoomVolumes(room);
          return (
            <div className="fe-banner ok">
              Пол {v.floor} м² · Периметр {v.perimeter} м · Стены нетто {v.wallsNet} м² · Плинтус {v.skirtingLength} м
            </div>
          );
        })()}
      </React.Fragment>
    );
  }

  function StepOpenings({ room, patchRoom }) {
    if (!room) return <div className="fe-empty">Сначала добавьте помещение</div>;
    const walls = CALC.ensureWalls(room);
    const openings = room.openings || [];

    function patchOp(oid, p) {
      patchRoom({ openings: openings.map(function (o) { return o.id === oid ? Object.assign({}, o, p) : o; }) });
    }

    function addOp(kind) {
      const def = DEMO.OPENING_KINDS.find(function (k) { return k.id === kind; }) || DEMO.OPENING_KINDS[0];
      patchRoom({ openings: openings.concat([{
        id: CALC.uid(), kind: def.id, widthM: def.defW, heightM: def.defH,
        depthM: CALC.DEFAULT_SLOPE_DEPTH[def.id] || 0.15, hasSlope: def.id === 'window' || def.id === 'door',
        deductFromWalls: true, wallId: walls[0]?.id,
      }]) });
    }

    return (
      <React.Fragment>
        <div className="fe-chips">
          <button type="button" className="fe-chip" onClick={() => addOp('window')}>+ Окно</button>
          <button type="button" className="fe-chip" onClick={() => addOp('door')}>+ Дверь</button>
          <button type="button" className="fe-chip" onClick={() => addOp('balcony_door')}>+ Балкон</button>
        </div>
        {openings.length === 0 && <div className="fe-empty" style={{ padding: 20 }}>Нет проёмов</div>}
        {openings.map(function (op) {
          const kindLabel = (DEMO.OPENING_KINDS.find(function (k) { return k.id === op.kind; }) || {}).label || op.kind;
          const w = +op.widthM || 0;
          const h = +op.heightM || 0;
          const depth = +op.depthM || CALC.DEFAULT_SLOPE_DEPTH[op.kind] || 0.15;
          const dedArea = op.deductFromWalls !== false ? w * h : 0;
          const slope = op.hasSlope ? (2 * h + w) * depth : 0;
          return (
            <div key={op.id} className="ms-op-card">
              <div className="ms-construct-head">
                <span>{kindLabel}</span>
                <button type="button" className="ms-del" onClick={() => patchRoom({ openings: openings.filter(function (x) { return x.id !== op.id; }) })}><Ic n="x" s={14} /></button>
              </div>
              <div className="fe-grid2">
                <Field label="Стена">
                  <select className="fe-input" value={op.wallId || ''} onChange={(e) => patchOp(op.id, { wallId: e.target.value })}>
                    <option value="">—</option>
                    {walls.map(function (wl) { return <option key={wl.id} value={wl.id}>{wl.label || ('Стена ' + wl.index)}</option>; })}
                  </select>
                </Field>
                <Field label="Ш × В, м">
                  <div style={{ display: 'flex', gap: 6 }}>
                    <input className="fe-input" type="number" step="0.05" value={op.widthM} onChange={(e) => patchOp(op.id, { widthM: +e.target.value })} placeholder="Ш" />
                    <input className="fe-input" type="number" step="0.05" value={op.heightM} onChange={(e) => patchOp(op.id, { heightM: +e.target.value })} placeholder="В" />
                  </div>
                </Field>
                <Field label="Глубина откоса, м"><input className="fe-input" type="number" step="0.01" value={op.depthM ?? ''} onChange={(e) => patchOp(op.id, { depthM: +e.target.value })} /></Field>
                <Field label="Отступ от угла, м"><input className="fe-input" type="number" step="0.05" value={op.offsetFromCornerM ?? ''} onChange={(e) => patchOp(op.id, { offsetFromCornerM: +e.target.value })} /></Field>
              </div>
              <label className="fe-check"><input type="checkbox" checked={op.hasSlope !== false && !!op.hasSlope} onChange={(e) => patchOp(op.id, { hasSlope: e.target.checked })} /><span>Считать откосы</span></label>
              <label className="fe-check"><input type="checkbox" checked={op.deductFromWalls !== false} onChange={(e) => patchOp(op.id, { deductFromWalls: e.target.checked })} /><span>Вычитать из площади стен</span></label>
              <div className="ms-op-preview">Вычитаемая площадь: <b>{CALC.roundUI(dedArea)} м²</b> · Откосы: <b>{CALC.roundUI(slope)} м²</b></div>
            </div>
          );
        })}
      </React.Fragment>
    );
  }

  function StepConstruct({ room, patchRoom }) {
    if (!room) return <div className="fe-empty">Сначала добавьте помещение</div>;
    const items = room.constructElements || [];

    function addKind(kind) {
      const label = (DEMO.CONSTRUCT_KINDS.find(function (k) { return k.id === kind; }) || {}).label;
      patchRoom({ constructElements: items.concat([{ id: CALC.uid(), kind: kind, label: label, count: 1, widthM: 0.4, depthM: 0.4, heightM: room.heightM || 2.75, lengthM: 1 }]) });
    }

    function patchEl(eid, p) {
      patchRoom({ constructElements: items.map(function (el) { return el.id === eid ? Object.assign({}, el, p) : el; }) });
    }

    return (
      <React.Fragment>
        <div className="fe-chips">
          {DEMO.CONSTRUCT_KINDS.slice(0, 6).map(function (k) {
            return <button key={k.id} type="button" className="fe-chip" onClick={() => addKind(k.id)}>+ {k.label}</button>;
          })}
        </div>
        <div className="ms-construct-grid">
          {items.map(function (el) {
            return (
              <div key={el.id} className="ms-construct-item">
                <div className="ms-construct-head">
                  <span>{el.label || el.kind}</span>
                  <button type="button" className="ms-del" onClick={() => patchRoom({ constructElements: items.filter(function (x) { return x.id !== el.id; }) })}><Ic n="x" s={14} /></button>
                </div>
                <div className="fe-grid2">
                  <Field label="Ширина, м"><input className="fe-input" type="number" step="0.05" value={el.widthM ?? ''} onChange={(e) => patchEl(el.id, { widthM: +e.target.value })} /></Field>
                  <Field label="Глубина, м"><input className="fe-input" type="number" step="0.05" value={el.depthM ?? ''} onChange={(e) => patchEl(el.id, { depthM: +e.target.value })} /></Field>
                  <Field label="Высота, м"><input className="fe-input" type="number" step="0.05" value={el.heightM ?? ''} onChange={(e) => patchEl(el.id, { heightM: +e.target.value })} /></Field>
                  <Field label="Кол-во"><input className="fe-input" type="number" min="1" step="1" value={el.count ?? 1} onChange={(e) => patchEl(el.id, { count: +e.target.value })} /></Field>
                </div>
                <Field label="Комментарий"><input className="fe-input" value={el.comment || ''} onChange={(e) => patchEl(el.id, { comment: e.target.value })} /></Field>
              </div>
            );
          })}
        </div>
      </React.Fragment>
    );
  }

  function StepEngineering({ room, patchRoom }) {
    if (!room) return <div className="fe-empty">Сначала добавьте помещение</div>;
    const pts = room.engineeringPoints || [];

    function getCount(kind) {
      const p = pts.find(function (x) { return x.kind === kind; });
      return p ? (p.count || 0) : 0;
    }

    function setCount(kind, count) {
      const rest = pts.filter(function (x) { return x.kind !== kind; });
      if (count > 0) patchRoom({ engineeringPoints: rest.concat([{ kind: kind, count: count }]) });
      else patchRoom({ engineeringPoints: rest });
    }

    return (
      <div className="fe-section">
        <h2 className="fe-section-title">Инженерия · {room.name}</h2>
        {DEMO.ENG_KINDS.filter(function (k) { return !k.bool; }).map(function (k) {
          return (
            <div key={k.id} className="ms-eng-row">
              <span>{k.label}</span>
              <div className="ms-stepper">
                <button type="button" onClick={() => setCount(k.id, Math.max(0, getCount(k.id) - 1))}>−</button>
                <span className="t-num">{getCount(k.id)}</span>
                <button type="button" onClick={() => setCount(k.id, getCount(k.id) + 1)}>+</button>
              </div>
            </div>
          );
        })}
        <div className="ms-eng-row">
          <span>Вентиляция</span>
          <label className="fe-check"><input type="checkbox" checked={getCount('vent_duct') > 0} onChange={(e) => setCount('vent_duct', e.target.checked ? 1 : 0)} /><span>Есть</span></label>
        </div>
      </div>
    );
  }

  function StepMedia({ obj, room, patchObj, patchRoom }) {
    const photos = room?.photos || {};
    const photoDone = DEMO.PHOTO_TAGS.filter(function (t) { return photos[t.id]; }).length;

    function togglePhoto(tag) {
      patchRoom({ photos: Object.assign({}, photos, { [tag]: !photos[tag] }) });
    }

    function addMockMedia(scope) {
      const list = scope === 'object' ? (obj.media || []) : (room.media || []);
      const item = { id: CALC.uid(), kind: 'photo', tag: 'plan', name: 'photo_' + Date.now() + '.jpg', capturedAt: new Date().toISOString() };
      if (scope === 'object') patchObj({ media: list.concat([item]) });
      else patchRoom({ media: list.concat([item]) });
    }

    return (
      <React.Fragment>
        <div className="fe-section">
          <h2 className="fe-section-title">Комментарий · {room?.name || 'объект'}</h2>
          {room ? (
            <textarea className="fe-input fe-textarea" rows={4} maxLength={2000} value={room.comment || ''} onChange={(e) => patchRoom({ comment: e.target.value })} placeholder="Особенности помещения, дефекты, пожелания" />
          ) : (
            <textarea className="fe-input fe-textarea" rows={3} value={obj.comment || ''} onChange={(e) => patchObj({ comment: e.target.value })} />
          )}
        </div>
        {room && (
          <div className="fe-section">
            <h2 className="fe-section-title">Фото помещения</h2>
            <p className="fe-section-sub">Прогресс: {photoDone}/{DEMO.PHOTO_TAGS.length}</p>
            <div className="ms-photo-grid">
              {DEMO.PHOTO_TAGS.map(function (t) {
                return (
                  <button key={t.id} type="button" className={'ms-photo-slot' + (photos[t.id] ? ' done' : '')} onClick={() => togglePhoto(t.id)}>
                    <Ic n={photos[t.id] ? 'check' : 'camera'} s={18} />
                    {t.label}
                  </button>
                );
              })}
            </div>
            <button type="button" className="fe-media-add" style={{ marginTop: 10 }} onClick={() => addMockMedia('room')}>
              <Ic n="upload" s={14} /> Загрузить фото (mock)
            </button>
            {(room.media || []).length > 0 && (
              <div className="fe-media-list" style={{ marginTop: 8 }}>
                {room.media.map(function (m) {
                  return <div key={m.id} className="fe-media-row"><Ic n="image" s={14} />{m.name}</div>;
                })}
              </div>
            )}
          </div>
        )}
        <div className="fe-section">
          <h2 className="fe-section-title">Фото объекта</h2>
          <button type="button" className="fe-media-add" onClick={() => addMockMedia('object')}>
            <Ic n="upload" s={14} /> Добавить фото фасада / подъезда (mock)
          </button>
          {(obj.media || []).length > 0 && (
            <div className="fe-media-list" style={{ marginTop: 8 }}>
              {obj.media.map(function (m) {
                return <div key={m.id} className="fe-media-row"><Ic n="image" s={14} />{m.name}</div>;
              })}
            </div>
          )}
        </div>
      </React.Fragment>
    );
  }

  function StepSummary({ result, coeffOverrides, setCoeffOverrides, onSubmit, embedded }) {
    const Button = resolveBtn(embedded);
    const blocked = result.blockers.length > 0;

    function setCoeff(id, val) {
      setCoeffOverrides(Object.assign({}, coeffOverrides, { [id]: val }));
    }

    return (
      <React.Fragment>
        {result.blockers.map(function (b, i) {
          return <div key={'b' + i} className="ms-alert block">{b.message}</div>;
        })}
        {result.warnings.map(function (w, i) {
          return <div key={'w' + i} className="ms-alert warn">{w.message}</div>;
        })}

        <div className="fe-section">
          <h2 className="fe-section-title">Сводка помещений</h2>
          <table className="ms-summary-table">
            <thead><tr><th>Помещение</th><th>Пол</th><th>Стены</th><th>Потолок</th></tr></thead>
            <tbody>
              {result.roomCalcs.map(function (r) {
                return (
                  <tr key={r.roomId}>
                    <td>{r.roomName}</td>
                    <td className="num">{r.floor}</td>
                    <td className="num">{r.wallsNet}</td>
                    <td className="num">{r.ceiling}</td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>

        <div className="fe-banner">
          Σ объект: <b>{result.objectTotals.area} м²</b> пол · <b>{result.objectTotals.wallsNet} м²</b> стены · <b>{result.objectTotals.wetArea} м²</b> мокрые · <b>{result.objectTotals.roomCount}</b> комн.
        </div>

        <div className="fe-section">
          <h2 className="fe-section-title">Коэффициенты (demo override)</h2>
          <div className="ms-coeff">
            {CALC.COEFF_META.filter(function (c) { return c.affects !== 'duration'; }).map(function (c) {
              const val = coeffOverrides[c.id] != null ? coeffOverrides[c.id] : result.coefficients[c.id];
              return (
                <label key={c.id}>
                  {c.label}
                  <input type="number" step="0.01" min={c.min} max={c.max} value={val} onChange={(e) => setCoeff(c.id, +e.target.value)} />
                </label>
              );
            })}
          </div>
        </div>

        {['demolition', 'rough', 'finish', 'materials'].map(function (blockKey) {
          const titles = { demolition: 'Демонтаж', rough: 'Черновые', finish: 'Чистовые', materials: 'Материалы' };
          const lines = result.estimateBlocks[blockKey] || [];
          return (
            <div key={blockKey} className="fe-section ms-block">
              <div className="ms-block-title">{titles[blockKey]}</div>
              {lines.map(function (ln) {
                const unit = ln.unit === 'm2' ? 'м²' : ln.unit === 'm' ? 'м' : 'шт';
                return (
                  <div key={ln.code} className="ms-block-line">
                    <span>{ln.label}</span>
                    <span className="qty t-num">{ln.roundedQty} {unit}</span>
                  </div>
                );
              })}
            </div>
          );
        })}

        {!embedded && (
          <div style={{ display: 'flex', gap: 8, flexWrap: 'wrap' }}>
            <Button variant="primary" disabled={blocked} onClick={onSubmit}>Отправить замер</Button>
            <Button variant="secondary" onClick={() => alert('Черновик сохранён в sessionStorage (demo)')}>Сохранить черновик</Button>
          </div>
        )}
      </React.Fragment>
    );
  }

  function MeasurementsWizard({ embedded }) {
    const Button = resolveBtn(embedded);
    const [session, setSession] = useState(function () { return loadSession(embedded); });
    const [step, setStep] = useState(0);
    const [roomId, setRoomId] = useState(function () { return (loadSession(embedded).object.rooms[0] || {}).id; });
    const [savedAt, setSavedAt] = useState(session.meta?.lastSavedAt);
    const [coeffOverrides, setCoeffOverrides] = useState(session.coeffOverrides || {});
    const [submitted, setSubmitted] = useState(false);
    const saveTimer = useRef(null);

    const obj = session.object;
    const rooms = obj.rooms || [];
    const activeRoom = rooms.find(function (r) { return r.id === roomId; }) || rooms[0];

    const persist = useCallback(function (next) {
      next.meta = Object.assign({}, next.meta, { lastSavedAt: new Date().toISOString() });
      setSession(next);
      if (!embedded) {
        try { sessionStorage.setItem(LS_KEY, JSON.stringify(next)); } catch (e) { /* ignore */ }
      }
      setSavedAt(next.meta.lastSavedAt);
    }, [embedded]);

    const patchObj = useCallback(function (p) {
      persist(Object.assign({}, session, { object: Object.assign({}, obj, p) }));
    }, [session, obj, persist]);

    const patchRoom = useCallback(function (p) {
      if (!activeRoom) return;
      const nextRooms = rooms.map(function (r) { return r.id === activeRoom.id ? Object.assign({}, r, p) : r; });
      patchObj({ rooms: nextRooms });
    }, [activeRoom, rooms, patchObj]);

    useEffect(function () {
      if (rooms.length && !rooms.find(function (r) { return r.id === roomId; })) setRoomId(rooms[0].id);
    }, [rooms, roomId]);

    const result = useMemo(function () {
      return CALC.calculateSession(Object.assign({}, session, { coeffOverrides: coeffOverrides }));
    }, [session, coeffOverrides]);

    function goNext() { setStep(function (s) { return Math.min(STEPS.length - 1, s + 1); }); }
    function goBack() { setStep(function (s) { return Math.max(0, s - 1); }); }

    function handleSubmit() {
      if (result.blockers.length) return;
      persist(Object.assign({}, session, { meta: Object.assign({}, session.meta, { status: 'submitted', submittedAt: new Date().toISOString() }), coeffOverrides: coeffOverrides }));
      setSubmitted(true);
    }

    if (submitted || session.meta?.status === 'submitted') {
      return (
        <div className={'ms-shell' + (embedded ? ' ms-embedded' : '')}>
          {!embedded && (
            <header className="ms-top">
              <a href="../Личные%20кабинеты.html"><Ic n="arrow-left" s={14} />Кабинеты</a>
              <div className="ms-top-title"><h1>Замер отправлен</h1><p>{obj.address}</p></div>
            </header>
          )}
          <div className="ms-page">
            <div className="fe-banner ok">Демо: aggregate → SiteIntake. Объём {result.objectTotals.area} м² передан в калькулятор.</div>
            <Button variant="primary" onClick={() => { sessionStorage.removeItem(LS_KEY); window.location.reload(); }}>Новый замер</Button>
            <a href="../lk/calculator.html?role=manager&deal=D-404" style={{ fontSize: 13, fontWeight: 700, color: 'var(--primary)' }}>Открыть калькулятор →</a>
          </div>
        </div>
      );
    }

    const stepId = STEPS[step].id;
    const needsRoom = ['walls', 'openings', 'construct', 'engineering', 'media'].indexOf(stepId) >= 0;

    return (
      <div className={'ms-shell' + (embedded ? ' ms-embedded' : '')}>
        {!embedded ? (
          <header className="ms-top">
            <a href="../Личные%20кабинеты.html"><Ic n="arrow-left" s={14} />Кабинеты</a>
            <div className="ms-top-title">
              <h1>Замер на объекте</h1>
              <p>{obj.address} · {obj.dealId}</p>
            </div>
            <span className="ms-save">Сохранено {fmtTime(savedAt)}</span>
          </header>
        ) : null}

        <div className="ms-page">
          {!embedded && <ReadonlyHeader obj={obj} compact />}
          {embedded && (
            <p className="ms-template-hint">Просмотр шагов геометрии. Адрес и данные сделки подставляются при замере на объекте.</p>
          )}

          <div className="ms-steps" role="tablist" aria-label="Шаги геометрии замера">
            <div className="ms-steps-meta">
              <span className="ms-steps-counter">Шаг {step + 1} из {STEPS.length}</span>
              <span className="ms-steps-current">{STEPS[step].label}</span>
            </div>
            <div className="ms-steps-track">
              {STEPS.map(function (s, i) {
                const isActive = i === step;
                const isDone = i < step;
                return (
                  <React.Fragment key={s.id}>
                    {i > 0 && <span className={'ms-steps-line' + (i <= step ? ' on' : '')} aria-hidden="true" />}
                    <button
                      type="button"
                      role="tab"
                      aria-selected={isActive}
                      aria-current={isActive ? 'step' : undefined}
                      title={s.label}
                      className={'ms-step' + (isActive ? ' active' : '') + (isDone ? ' done' : '')}
                      onClick={() => setStep(i)}
                    >
                      <span className="ms-step-index" aria-hidden="true">
                        {isDone ? (
                          <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="3" strokeLinecap="round" strokeLinejoin="round"><polyline points="20 6 9 17 4 12" /></svg>
                        ) : (i + 1)}
                      </span>
                      <span className="ms-step-label">{s.label}</span>
                    </button>
                  </React.Fragment>
                );
              })}
            </div>
            <div className="ms-steps-progress" aria-hidden="true">
              <div className="ms-steps-progress-fill" style={{ width: ((step + 1) / STEPS.length * 100) + '%' }} />
            </div>
          </div>

          {needsRoom && (
            <MsRoomStrip rooms={rooms} activeId={activeRoom?.id} onSelect={setRoomId} />
          )}

          {stepId === 'rooms' && (
            <StepRooms
              obj={obj}
              patchObj={patchObj}
              embedded={embedded}
              onGoWalls={embedded ? null : function (rid) { setRoomId(rid); setStep(1); }}
            />
          )}
          {needsRoom && activeRoom && (
            <div className="ms-room-workspace">
              {stepId === 'walls' && <StepWalls room={activeRoom} patchRoom={patchRoom} />}
              {stepId === 'openings' && <StepOpenings room={activeRoom} patchRoom={patchRoom} />}
              {stepId === 'construct' && <StepConstruct room={activeRoom} patchRoom={patchRoom} />}
              {stepId === 'engineering' && <StepEngineering room={activeRoom} patchRoom={patchRoom} />}
              {stepId === 'media' && <StepMedia obj={obj} room={activeRoom} patchObj={patchObj} patchRoom={patchRoom} />}
            </div>
          )}
          {stepId === 'summary' && (
            <StepSummary result={result} coeffOverrides={coeffOverrides} setCoeffOverrides={setCoeffOverrides} onSubmit={handleSubmit} embedded={embedded} />
          )}
        </div>

        <footer className="ms-foot">
          <div className="ms-foot-inner">
            {step > 0 ? <Button variant="secondary" onClick={goBack}>Назад</Button> : <span />}
            {step < STEPS.length - 1 ? (
              <Button variant="primary" onClick={goNext}>Далее</Button>
            ) : embedded ? (
              <span className="ms-template-hint-foot">Отправка доступна в анкете на объекте</span>
            ) : (
              <Button variant="primary" disabled={result.blockers.length > 0} onClick={handleSubmit}>Отправить замер</Button>
            )}
          </div>
        </footer>
      </div>
    );
  }

  window.MeasurementsWizard = MeasurementsWizard;

  const root = document.getElementById('root');
  if (root && !window.__MEASUREMENTS_EMBED_ONLY) {
    const mount = window.mountWhenReady || function (render) {
      ReactDOM.createRoot(root).render(render());
    };
    mount(function () { return <MeasurementsWizard embedded={false} />; });
  }
})();
