/* calc2-rooms-panel.jsx — смета по комнатам в калькуляторе менеджера (KSH-502, спека 2026-11-01-client-estimate-by-rooms §5).
   Вкладки над деревом: «Вся смета» · комнаты (название, «этаж N», галочка фильтра комнат) · «Общее по объекту» — только когда у сметы есть
   комнаты (Calc2.hasRooms). Вкладка комнаты: шапка (пол, стены, периметр, окна, точки, сумма), работы по этапам с правкой объёма
   (Calc2.setRoomQty), «снять / вернуть» (setRoomOff), доля у работ «за работу» (share, только чтение), «+ работа из каталога» (addToRoom),
   материалы комнаты — только чтение. «Вся смета» — дерево сметы (calc2-app.jsx, calc2-tree.jsx).
   Кнопки «Разбить по комнатам замера» / «Обновить комнаты из замера» и диалог «было / стало» — Calc2RoomsSyncBar (calc2-assembly-panel.jsx).
   Правят комнаты менеджер и super (право preset / qty), остальные роли — только чтение; отправленная и замороженная смета — только чтение.
   window.Calc2RoomsPanel({ est, setEst, role, catalog, intake, tab, onTab, dealId }); Calc2RoomsPanel.activeTab(est, tab) → 'all' | 'obj' | id комнаты */
(function () {
  const C = window.Calc2;
  const { Ic } = window;
  const { useState } = React;

  const num = (n) => String(Math.round((+n || 0) * 100) / 100).replace('.', ',');
  const U = (u) => C.unitLabel(u);
  const rooms = (est) => (Array.isArray(est.rooms) ? est.rooms.filter(Boolean) : []);

  // вкладка, которая действует: нет комнат или комнату убрали (обновление из замера) — «Вся смета»
  function activeTab(est, tab) {
    if (!C.hasRooms(est)) return 'all';
    if (tab === 'obj') return 'obj';
    return rooms(est).some((r) => r.id === tab) ? tab : 'all';
  }

  /* работу менеджер хотел в смету (включена, зависимости выполнены), даже если объёма пока нет */
  function wantedOn(est, node, s) {
    if (s.included) return true;
    const ov = (est.nodes && est.nodes[node.id]) || {};
    const inc = C.effectiveInc(node);
    return ov.included != null ? !!ov.included : (inc === 'conditional' ? !s.exclusionReason : C.defaultIncluded(node));
  }

  /* строки комнаты по этапам: работа входит в комнату (roomParts) или снята в ней менеджером (rooms[id].off) — тогда строка «вернуть».
     [{ stage, rows: [{ node, s, part, parts, i, off, mode }], cands: [...] }] — cands: работы, которые можно добавить в комнату */
  function roomSections(est, catalog, room, role) {
    const rid = room.id;
    const sections = [], at = {};
    const sec = (st) => at[st.id] || (at[st.id] = (sections.push({ stage: st, rows: [], cands: [] }), sections[sections.length - 1]));
    C.leafLines(catalog).forEach((l) => {
      const node = l.node;
      if (!C.visibleTo(node, role)) return;
      const mode = C.roomMode(node, l.stage, l.kind === 'sub' ? l.task : null);
      if (mode === 'object') return;
      const s = C.nodeState(est, node, catalog, l.stage.id);
      const ed = ((((est.nodes || {})[node.id] || {}).rooms || {})[rid]) || {};
      const parts = C.roomParts(est, node, catalog);
      const i = parts.findIndex((p) => p.roomId === rid);
      const on = wantedOn(est, node, s);
      if (i >= 0) sec(l.stage).rows.push({ node, s, part: parts[i], parts, i, off: false, mode });
      // volume-работа добавлена в комнату без размера по замеру (метрика 0): движок часть не отдаёт — строка с пустым объёмом для ввода
      else if (ed.on && !ed.off && on && mode === 'volume' && !room.off) sec(l.stage).rows.push({ node, s, part: { roomId: rid, qty: 0, share: 0, auto: 0, manual: false, added: true, empty: true }, parts, i: -1, off: false, mode });
      else if (ed.off && on) sec(l.stage).rows.push({ node, s, part: null, parts, i, off: true, mode });
      else if (on && !ed.off) sec(l.stage).cands.push({ node, s });
    });
    return sections;
  }

  function RoomHead({ est, catalog, room }) {
    const m = room.metrics || {};
    const pts = Object.values(m.points || {}).reduce((a, x) => a + (+x || 0), 0);
    const total = C.roomTotals(est, catalog)[room.id] || 0;
    const stat = (label, val) => <span className="cc-rm-stat"><span>{label}</span><b>{val}</b></span>;
    // клиент отказался от всей комнаты (@roomId, KSH-502) — только чтение, снимают в кабинете клиента или возвратом сметы в работу
    const d = (est.decisions || {})['@' + room.id];
    const refused = room.id !== 'object' && d && d.state === 'rejected';
    return (
      <div className="cc-rm-head">
        {stat('Пол', num(m.floor) + ' м²')}
        {stat('Стены', num(m.wallsNet) + ' м²')}
        {stat('Периметр', num(m.perimeter) + ' м')}
        {stat('Окна', num(m.windows))}
        {stat('Точки', num(pts))}
        <span className="cc-rm-stat cc-rm-sum"><span>Сумма комнаты</span><b>{C.rub(total)} ₽</b></span>
        {refused && <span className="cc-decide-badge rejected" title="Решение клиента">{'Клиент отказался от комнаты' + (d.comment ? ': ' + d.comment : '')}</span>}
      </div>
    );
  }

  function RoomView({ est, setEst, role, catalog, room }) {
    const DraftNum = window.Calc2DraftNum;
    const [pick, setPick] = useState(false);
    const locked = C.isLocked(est.status) || !!est.frozen;
    const staff = role === 'manager' || role === 'super';
    const canQty = staff && !locked && C.can(role, 'qty', est.status);
    const canPreset = staff && !locked && C.can(role, 'preset', est.status);
    const mut = (fn) => { try { setEst(fn()); } catch (e) { alert(e.message); } };
    const sections = roomSections(est, catalog, room, role);
    const rowsN = sections.reduce((n, x) => n + x.rows.length, 0);
    const cands = sections.filter((x) => x.cands.length);
    return (
      <div className="cc-rm" data-room={room.id}>
        <RoomHead est={est} catalog={catalog} room={room} />
        {room.off && <div className="cc-rm-note">Комната вне сметы: включите галочку у вкладки, и её работы войдут в расчёт.</div>}
        {!room.off && rowsN === 0 && <div className="cc-rm-note">В этой комнате пока нет работ: добавьте из каталога.</div>}
        {sections.filter((x) => x.rows.length).map((x) => (
          <div className="cc-rm-stage" key={x.stage.id}>
            <div className="cc-rm-stage-h">{x.stage.name}</div>
            {x.rows.map((r) => {
              const { node, s, part } = r;
              const share = r.mode === 'share';
              const amount = r.off ? 0 : share && r.i >= 0 ? C.shareAmounts(Math.round(s.price * s.qty), r.parts)[r.i] : Math.round(s.price * part.qty);
              return (
                <div className="cc-rm-item" key={node.id}>
                  <div className={'cc-rm-row' + (r.off ? ' is-off' : '')}>
                    <span className="cc-rm-name">
                      {node.name}
                      {r.off && <span className="cc-pill ro">снята в комнате</span>}
                      {!r.off && part.added && <span className="cc-pill ro" title="Добавлена в комнату вручную">добавлена</span>}
                      {!r.off && part.empty && <span className="cc-pill ro" title="В замере нет размера для этой работы: введите объём">нет объёма по замеру</span>}
                      {!r.off && !share && part.manual && <span className="cc-pill ro" title="Объём введён вручную, не по замеру">вручную</span>}
                    </span>
                    {!r.off && !share && (
                      <span className={'cc-num' + (canQty ? '' : ' ro')} title="Объём в комнате">
                        <DraftNum value={part.qty} disabled={!canQty} onCommit={(n) => mut(() => C.setRoomQty(est, node.id, room.id, n))} />
                        <span>{U(node.unit)}</span>
                      </span>
                    )}
                    {!r.off && !share && part.manual && canQty && (
                      <button type="button" className="cc-iconbtn" title="Вернуть объём по замеру" onClick={() => mut(() => C.setRoomQty(est, node.id, room.id, null))}>↺</button>
                    )}
                    {!r.off && share && <span className="cc-pill ro cc-rm-share" title="Работа «за работу»: доля комнаты по площади пола, не правится">{'доля ' + Math.round(part.share * 100) + '%'}</span>}
                    {!r.off && !share && <span className="cc-rm-price" title="Цена за единицу">{C.rub(s.price)} ₽</span>}
                    <span className="cc-rm-total">{r.off ? '—' : C.rub(amount) + ' ₽'}</span>
                    {canPreset && (
                      <button type="button" className="cc-rm-toggle" title="В комнате: снять или вернуть работу" onClick={() => mut(() => C.setRoomOff(est, node.id, room.id, !r.off))}>{r.off ? 'вернуть' : 'снять'}</button>
                    )}
                  </div>
                  {!r.off && <RoomMats est={est} node={node} roomId={room.id} />}
                </div>
              );
            })}
          </div>
        ))}
        {!room.off && <RoomPackages est={est} setEst={setEst} catalog={catalog} room={room} canEdit={canQty} />}
        {canPreset && !room.off && (
          <div className="cc-rm-add">
            <button type="button" className="cc-addrow" onClick={() => setPick(!pick)}><Ic n="plus" s={14} />+ работа из каталога</button>
            {pick && (
              <div className="cc-rm-addlist">
                {cands.length === 0 && <div className="cc-rm-note">Все подходящие работы сметы уже в комнате.</div>}
                {cands.map((x) => (
                  <div key={x.stage.id}>
                    <div className="cc-rm-stage-h">{x.stage.name}</div>
                    {x.cands.map((c) => (
                      <button type="button" className="cc-rm-cand" key={c.node.id} onClick={() => { mut(() => C.addToRoom(est, c.node.id, room.id)); setPick(false); }}>{c.node.name}</button>
                    ))}
                  </div>
                ))}
              </div>
            )}
          </div>
        )}
      </div>
    );
  }

  /* «Пакеты» комнаты (KSH-505, часть 2): пакеты сметы, раскладка которых (Calc2.packageRooms) включает эту комнату — количество в комнате
     (менеджер и super правят: Calc2.setPackageRoomQty, Σ комнат = количество пакета; ↺ — снова авто-раскладка) и сумма комнаты
     (доли шагов, Calc2.packageRoomLines). Отказ клиента от пакета в комнате (pkg:<id>@roomId) — плашкой, только чтение */
  function RoomPackages({ est, setEst, catalog, room, canEdit }) {
    const DraftNum = window.Calc2DraftNum;
    const list = (C.packageRoomLines ? C.packageRoomLines(est, catalog) : []).filter((pk) => pk.parts.some((p) => p.roomId === room.id));
    if (!list.length) return null;
    const mut = (fn) => { try { setEst(fn()); } catch (e) { alert(e.message); } };
    const reset = (pk) => mut(() => C.resetPackageRooms(est, pk.id));
    return (
      <div className="cc-rm-stage cc-rm-pkgs" data-room-pkgs={room.id}>
        <div className="cc-rm-stage-h">Пакеты</div>
        {list.map((pk) => {
          const part = pk.parts.find((p) => p.roomId === room.id);
          const am = pk.rooms.find((x) => x.roomId === room.id);
          const d = (est.decisions || {})['pkg:' + pk.id + '@' + room.id];
          const manual = pk.parts.some((p) => p.manual);
          return (
            <div className="cc-rm-item" key={pk.id} data-pkg={pk.id}>
              <div className="cc-rm-row">
                <span className="cc-rm-name">
                  <span style={{ color: pk.color }}>{pk.icon}</span> {pk.name}
                  {manual && <span className="cc-pill ro" title="Количество по комнатам задано вручную">вручную</span>}
                  {d && d.state === 'rejected' && <span className="cc-decide-badge rejected" title="Решение клиента">{'Клиент отказался' + (d.comment ? ': ' + d.comment : '')}</span>}
                </span>
                <span className={'cc-num' + (canEdit ? '' : ' ro')} title="Количество пакета в комнате">
                  {DraftNum
                    ? <DraftNum value={part.qty} disabled={!canEdit} onCommit={(n) => mut(() => C.setPackageRoomQty(est, pk.id, room.id, n))} />
                    : <b>{part.qty}</b>}
                  <span>шт</span>
                </span>
                {manual && canEdit && (
                  <button type="button" className="cc-iconbtn" title="Вернуть раскладку по умолчанию (по одному в комнату)" onClick={() => reset(pk)}>↺</button>
                )}
                <span className="cc-rm-total">{C.rub(am ? am.amount : 0) + ' ₽'}</span>
              </div>
            </div>
          );
        })}
      </div>
    );
  }

  // материалы комнаты: часть материалов работы по объёму комнаты, только чтение
  function RoomMats({ est, node, roomId }) {
    let mats = [];
    try { mats = C.nodeMaterials(est, node.id, est.project.area, roomId) || []; } catch (e) { mats = []; }
    if (!mats.length) return null;
    return (
      <div className="cc-rm-mats" title="Материалы комнаты — только чтение">
        {mats.map((m) => <span className="cc-rm-mat" key={m.bindingMatId || m.id}>{m.name + ' — ' + num(m.qty) + ' ' + m.unit}</span>)}
      </div>
    );
  }

  // «Общее по объекту»: объектные работы, работы «за работу» без комнат и объектная часть комнатных; правятся в «Вся смета»
  function ObjectView({ est, catalog, role }) {
    const sections = [], at = {};
    C.leafLines(catalog).forEach((l) => {
      const node = l.node;
      if (!C.visibleTo(node, role)) return;
      const s = C.nodeState(est, node, catalog, l.stage.id);
      if (!s.included) return;
      const mode = C.roomMode(node, l.stage, l.kind === 'sub' ? l.task : null);
      const ov = (est.nodes && est.nodes[node.id]) || {};
      let qty = null;
      if (mode === 'object') qty = s.qty;
      else if (mode === 'share') { if (!C.roomParts(est, node, catalog).length) qty = s.qty; }
      else if (+ov.objQty > 0) qty = +ov.objQty;
      if (qty == null) return;
      const sec = at[l.stage.id] || (at[l.stage.id] = (sections.push({ stage: l.stage, rows: [] }), sections[sections.length - 1]));
      sec.rows.push({ node, s, qty, obj: mode !== 'object' && mode !== 'share', need: role !== 'client' && C.qtyNeedsInput(est, node, catalog) });
    });
    return (
      <div className="cc-rm">
        <div className="cc-rm-note">Объектные работы и «Общее» комнатных работ. Правятся в «Вся смета».</div>
        {sections.length === 0 && <div className="cc-rm-note">Работ по объекту нет.</div>}
        {sections.map((x) => (
          <div className="cc-rm-stage" key={x.stage.id}>
            <div className="cc-rm-stage-h">{x.stage.name}</div>
            {x.rows.map((r) => (
              <div className="cc-rm-item" key={r.node.id}>
                <div className="cc-rm-row">
                  <span className="cc-rm-name">{r.node.name}{r.obj && <span className="cc-pill ro">Общее</span>}{r.need && <span className="cc-pill cc-qty-need" title="Цена за единицу, объём не по размерам комнат — введите объём в «Вся смета»">Укажите объём</span>}</span>
                  <span className="cc-rm-qty">{num(r.qty)} {U(r.node.unit)}</span>
                  <span className="cc-rm-total">{C.rub(Math.round(r.s.price * r.qty))} ₽</span>
                </div>
              </div>
            ))}
          </div>
        ))}
      </div>
    );
  }

  function Calc2RoomsPanel({ est, setEst, role, catalog, intake, tab, onTab, dealId }) {
    const Bar = window.Calc2RoomsSyncBar;
    if (role === 'client') return null;
    const has = C.hasRooms(est);
    const cur = activeTab(est, tab);
    const locked = C.isLocked(est.status) || !!est.frozen;
    const canFilter = (role === 'manager' || role === 'super') && !locked && C.can(role, 'preset', est.status);
    const go = (id) => { if (onTab) onTab(id); };
    const room = has ? rooms(est).find((r) => r.id === cur) : null;
    return (
      <div className="cc-rooms">
        {Bar && <Bar est={est} setEst={setEst} role={role} dealId={dealId || est.project.dealId} intake={intake} />}
        {has && (
          <div className="cc-rtabs" role="tablist" aria-label="Смета по комнатам">
            <button type="button" role="tab" aria-selected={cur === 'all'} className={'cc-rtab' + (cur === 'all' ? ' on' : '')} onClick={() => go('all')}>Вся смета</button>
            {rooms(est).map((r) => (
              <span key={r.id} className={'cc-rtab cc-rtab-room' + (cur === r.id ? ' on' : '') + (r.off ? ' is-off' : '')}>
                <input type="checkbox" checked={!r.off} disabled={!canFilter} title={r.off ? 'Комната вне сметы: вернуть' : 'Комната в смете: снять'} aria-label={'В смете: ' + r.name}
                  onChange={(e) => { try { setEst(C.setRoomFilter(est, r.id, !e.target.checked)); } catch (err) { alert(err.message); } }} />
                <button type="button" role="tab" aria-selected={cur === r.id} onClick={() => go(r.id)}>
                  {r.name}{r.floor != null && <small>{'этаж ' + r.floor}</small>}
                </button>
              </span>
            ))}
            <button type="button" role="tab" aria-selected={cur === 'obj'} className={'cc-rtab' + (cur === 'obj' ? ' on' : '')} onClick={() => go('obj')}>Общее по объекту</button>
          </div>
        )}
        {has && cur === 'obj' && <ObjectView est={est} catalog={catalog} role={role} />}
        {room && <RoomView est={est} setEst={setEst} role={role} catalog={catalog} room={room} />}
      </div>
    );
  }

  Calc2RoomsPanel.activeTab = activeTab;
  window.Calc2RoomsPanel = Calc2RoomsPanel;
})();
