/* ============================================================
   Замер — структурированная загрузка фото по типам.
   Типы фото: стена 1-4, план комнаты, общий вид, деталь.
   ============================================================ */

const Measurements = (function () {
  const STORAGE_KEY = "zov-measurement-draft-v2";

  // Типы фото — в соответствии с чек-листом ЗАМЕРОВ
  const PHOTO_KINDS = [
    { key: "wall1",   label: "Стена 1" },
    { key: "wall2",   label: "Стена 2" },
    { key: "wall3",   label: "Стена 3" },
    { key: "wall4",   label: "Стена 4" },
    { key: "plan",    label: "План комнаты" },
    { key: "general", label: "Общий вид" },
    { key: "detail",  label: "Деталь" },
  ];

  function kindLabel(k) {
    return (PHOTO_KINDS.find(p => p.key === k) || {}).label || k;
  }

  // Фото держим только в памяти
  let photos = []; // Array<{ dataUrl, kind }>

  let state = loadState();
  let root = null;
  let measurementId = ""; // если задан — update-mode (закрытие заявки)
  let prefilledClient = null;
  let pickedClient    = null; // клиент из пикера (только для нового замера)

  function loadState() {
    try {
      const raw = localStorage.getItem(STORAGE_KEY);
      if (raw) return { ...defaultState(), ...JSON.parse(raw) };
    } catch (e) {}
    return defaultState();
  }

  function defaultState() {
    const todayStr = new Date().toISOString().slice(0, 10);
    return {
      client_name: "",
      client_phone: "",
      address: "",
      notes: "",
      // Общая инфа замера. zamer_no подгружается из бэка автоматически,
      // floor_base убран — он на самих фото с замером.
      zamer_no: "",
      zamer_date: todayStr,
    };
  }

  function saveState() {
    try { localStorage.setItem(STORAGE_KEY, JSON.stringify(state)); } catch (e) {}
  }

  function reset() {
    state = defaultState();
    saveState();
    photos = [];
    prefilledClient = null;
    pickedClient    = null;
  }

  /* ===================== Mount + Render ===================== */

  function mount(container) {
    root = container;
    document.body.classList.remove("has-bottom-nav");
    const oldNav = document.getElementById("bottom-nav");
    if (oldNav) oldNav.remove();

    photos = [];
    measurementId = "";
    prefilledClient = null;
    pickedClient    = null;

    const hashMatch = (location.hash.split("?")[1] || "");
    const fragQp = new URLSearchParams(hashMatch);
    const mid = fragQp.get("id") || new URLSearchParams(location.search).get("measurement_id") || "";

    // Спецроут #/measure/checklist — показать чек-лист
    if (location.hash.startsWith("#/measure/checklist")) {
      renderChecklist();
      return;
    }
    if (mid) {
      measurementId = mid;
      loadRequestAndStart();
      return;
    }
    render();
  }

  async function loadRequestAndStart() {
    root.innerHTML = "";
    root.appendChild(renderHeader("Закрыть заявку"));
    root.appendChild(el(`<div class="loader-inline"><div class="spinner"></div></div>`));
    try {
      const res = await fetch(`${BACKEND_URL}/api/measurement_detail`, {
        method: "POST",
        body: JSON.stringify({
          initData: tg?.initData || "",
          initDataUnsafe: tg?.initDataUnsafe || null,
          measurement_id: measurementId,
        }),
      });
      const data = await res.json();
      if (data.error) {
        root.innerHTML = "";
        root.appendChild(renderHeader("Ошибка"));
        root.appendChild(el(`<div class="error">${data.error}</div>`));
        return;
      }
      prefilledClient = {
        name: data.client_name || "",
        phone: data.client_phone || "",
        address: data.address || "",
      };
      render();
    } catch (e) {
      root.innerHTML = "";
      root.appendChild(renderHeader("Ошибка"));
      root.appendChild(el(`<div class="error">Сеть: ${e.message}</div>`));
    }
  }

  function render() {
    if (!root) return;
    root.innerHTML = "";
    root.appendChild(renderHeader(measurementId ? "Закрыть заявку" : "Новый замер"));
    const screen = el(`<div class="podbor-screen"></div>`);
    root.appendChild(screen);
    screen.appendChild(renderForm());
  }

  function renderHeader(title) {
    const h = el(`
      <header class="podbor-header">
        <button class="podbor-back" aria-label="Назад">${ICONS.arrow_left || "‹"}</button>
        <div class="podbor-title">${escHtml(title)}</div>
        <button class="podbor-help" id="openChecklist" aria-label="Чек-лист">📋</button>
      </header>
    `);
    h.querySelector(".podbor-back").addEventListener("click", () => {
      location.hash = "";
      location.reload();
    });
    h.querySelector("#openChecklist").addEventListener("click", () => {
      location.hash = "#/measure/checklist";
    });
    return h;
  }

  /* ===================== Главный экран ===================== */

  function renderForm() {
    const isUpdate = !!measurementId && prefilledClient;
    const clientBlock = isUpdate ? renderClientReadOnly() : renderClientPicker();

    const node = el(`
      <section class="podbor-step">
        <h2 class="display-title">${isUpdate ? "Фото<br><span class=\"accent\">с замера</span>" : "Новый<br><span class=\"accent\">замер</span>"}</h2>
        <p class="lede">${isUpdate
          ? "Загружайте фото по чек-листу — каждая стена отдельно. Чертёж сделаем по фото."
          : "Заполните клиента, дату и загрузите фото по чек-листу. Откройте 📋 чтобы посмотреть как правильно снимать."}</p>

        <div id="clientBlock"></div>

        <div class="section-head" style="margin-top:18px;"><span class="label">📐 Общая информация</span></div>
        <div class="form-row two-col">
          <label class="field">
            <span class="field-label">№ замера</span>
            <input type="text" data-bind="zamer_no" id="zamerNoInput" value="${escAttr(state.zamer_no)}" placeholder="…">
            <span class="field-hint" id="zamerNoHint">Подбираем следующий…</span>
          </label>
          <label class="field">
            <span class="field-label">Дата замера</span>
            <input type="date" data-bind="zamer_date" value="${escAttr(state.zamer_date)}">
          </label>
        </div>

        <div class="section-head" style="margin-top:18px;">
          <span class="label">📷 Фото замера</span>
          <a class="more" id="openChecklist2" style="cursor:pointer;">Чек-лист</a>
        </div>
        <p class="muted" style="font-size:12px;margin:-4px 0 8px;">
          Для каждого фото выберите тип. По чек-листу: каждая стена отдельно + план + общие виды.
        </p>
        <div class="photo-uploader">
          <label class="photo-add-btn" for="photoInput">
            <span class="photo-add-ico">＋</span>
            <span class="photo-add-label">Добавить фото</span>
            <span class="photo-add-hint">камера или галерея · до 30 шт</span>
          </label>
          <input id="photoInput" type="file" accept="image/*" capture="environment" multiple hidden>
        </div>
        <div class="photo-list-tagged" id="photoList"></div>

        <div class="form-row" style="margin-top:18px;">
          <label class="field">
            <span class="field-label">Заметки (голосом или текстом)</span>
            <textarea data-bind="notes" id="zamerNotes" rows="3" placeholder="особенности доступа, газ/электро, что важно учесть">${escHtml(state.notes || "")}</textarea>
            <div class="note-actions" style="margin-top:6px;">
              <button class="btn-mic" id="zamerMic" type="button">🎤 Диктовать</button>
              <span class="note-status" id="zamerMicStatus"></span>
            </div>
          </label>
        </div>

        <div class="podbor-cta-row" style="margin-top:20px;">
          <button class="btn-primary" id="submitBtn">${isUpdate ? "Закрыть заявку" : "Сохранить замер"}</button>
        </div>

        <div id="submitResult" class="submit-result"></div>
      </section>
    `);

    node.querySelector("#clientBlock").appendChild(clientBlock);
    bindInputs(node);
    bindPhotoInput(node);

    node.querySelector("#openChecklist2").addEventListener("click", () => {
      location.hash = "#/measure/checklist";
    });
    node.querySelector("#submitBtn").addEventListener("click", () => onSubmit(node));

    // Голосовой ввод заметок
    setupVoiceMic(
      node.querySelector("#zamerMic"),
      node.querySelector("#zamerNotes"),
      node.querySelector("#zamerMicStatus"),
      (text) => { state.notes = text; saveState(); },
    );

    // Подгружаем следующий № замера если поле пустое
    if (!state.zamer_no) {
      fetchNextZamerNo(node);
    } else {
      const hint = node.querySelector("#zamerNoHint");
      if (hint) hint.textContent = "Можно переписать вручную";
    }

    return node;
  }

  /* ===================== Голосовой ввод заметок ===================== */
  // continuous=false + авто-рестарт по фразам — исключает дубли на Android/iOS Chrome
  function setupVoiceMic(micBtn, textarea, statusEl, onChange) {
    if (!micBtn || !textarea) return;
    const SR = window.SpeechRecognition || window.webkitSpeechRecognition;
    if (!SR) {
      micBtn.disabled = true;
      micBtn.title = "Браузер не поддерживает голосовой ввод";
      micBtn.style.opacity = "0.5";
      if (statusEl) statusEl.textContent = "недоступно в этом браузере";
      return;
    }

    let active = false;
    let baseText = "";
    let curRec = null;

    function startPhrase() {
      let rec;
      try {
        rec = new SR();
        rec.lang = "ru-RU";
        rec.continuous    = false;
        rec.interimResults = true;
      } catch (e) {
        if (statusEl) statusEl.textContent = "Микрофон недоступен: " + e.message;
        active = false; micBtn.classList.remove("rec"); micBtn.textContent = "🎤 Диктовать";
        return;
      }
      curRec = rec;

      rec.onresult = (ev) => {
        let fin = "", itr = "";
        for (let i = 0; i < ev.results.length; i++) {
          const t = ev.results[i][0].transcript.trim();
          if (!t) continue;
          if (ev.results[i].isFinal) fin += (fin ? " " : "") + t;
          else              itr += (itr ? " " : "") + t;
        }
        const shown = fin || itr;
        textarea.value = baseText + (baseText && shown ? " " : "") + shown;
      };

      rec.onend = () => {
        baseText = textarea.value.trim();
        if (active) {
          startPhrase();
        } else {
          micBtn.classList.remove("rec");
          micBtn.textContent = "🎤 Диктовать";
          if (statusEl && statusEl.textContent === "Слушаю...") statusEl.textContent = "";
          if (onChange) onChange(textarea.value || "");
          haptic && haptic("impact");
        }
      };

      rec.onerror = (ev) => {
        if (ev.error === "no-speech") return;
        if (statusEl) statusEl.textContent = "Ошибка: " + (ev.error || "неизвестно");
        active = false; micBtn.classList.remove("rec"); micBtn.textContent = "🎤 Диктовать";
      };

      try { rec.start(); } catch (e) {
        if (statusEl) statusEl.textContent = "Не запустить: " + e.message;
        active = false; micBtn.classList.remove("rec"); micBtn.textContent = "🎤 Диктовать";
      }
    }

    micBtn.addEventListener("click", () => {
      if (active) {
        active = false;
        curRec?.stop();
        return;
      }
      active   = true;
      baseText = (textarea.value || "").trim();
      micBtn.classList.add("rec");
      micBtn.textContent = "⏹ Стоп";
      if (statusEl) statusEl.textContent = "Слушаю...";
      haptic && haptic("impact");
      startPhrase();
    });
  }

  async function fetchNextZamerNo(node) {
    try {
      const res = await fetch(`${BACKEND_URL}/api/measurement_next_no`, {
        method: "POST",
        body: JSON.stringify({
          initData: tg?.initData || "",
          initDataUnsafe: tg?.initDataUnsafe || null,
        }),
      });
      const data = await res.json();
      const hint = node.querySelector("#zamerNoHint");
      const input = node.querySelector("#zamerNoInput");
      if (data.ok && data.next_no && input && !state.zamer_no) {
        input.value = String(data.next_no);
        state.zamer_no = String(data.next_no);
        saveState();
        if (hint) hint.textContent = "Подобран автоматически — можно изменить";
      } else if (hint) {
        hint.textContent = "Введите номер вручную";
      }
    } catch (e) {
      const hint = node.querySelector("#zamerNoHint");
      if (hint) hint.textContent = "Введите номер вручную";
    }
  }

  function renderClientReadOnly() {
    return el(`
      <div class="block">
        <div class="kv"><span>Клиент</span>&nbsp;<strong>${escHtml(prefilledClient.name || "—")}</strong></div>
        ${prefilledClient.phone ? `<div class="kv"><span>Телефон</span>&nbsp;<strong>${escHtml(prefilledClient.phone)}</strong></div>` : ""}
        ${prefilledClient.address ? `<div class="kv"><span>Адрес</span>&nbsp;<strong>${escHtml(prefilledClient.address)}</strong></div>` : ""}
      </div>
    `);
  }

  /* ===================== Пикер клиента ===================== */

  function renderClientPicker() {
    const wrap = el(`
      <div class="client-picker-wrap">
        <div class="form-row" id="pcChoiceRow"></div>
        <div class="form-row">
          <label class="field">
            <span class="field-label">Адрес объекта</span>
            <input type="text" data-bind="address" id="pcAddr"
                   value="${escAttr(state.address)}"
                   placeholder="СПб, пр. Просвещения, д. 87, кв. 12">
          </label>
        </div>
      </div>
    `);

    const choiceRow = wrap.querySelector("#pcChoiceRow");
    const addrInput = wrap.querySelector("#pcAddr");

    addrInput.addEventListener("input", e => {
      state.address = e.target.value;
      saveState();
    });

    function refresh() {
      choiceRow.innerHTML = "";
      if (pickedClient) {
        const card = el(`
          <div class="picker-chosen-card">
            <div class="picker-chosen-info">
              <div class="picker-chosen-name">${escHtml(pickedClient.client_name)}</div>
              <div class="picker-chosen-sub">
                ${escHtml(pickedClient.client_phone || "")}${pickedClient.contract_no ? " · дог. " + escHtml(pickedClient.contract_no) : ""}
              </div>
            </div>
            <button class="picker-change-btn" type="button">Изменить</button>
          </div>
        `);
        card.querySelector(".picker-change-btn").addEventListener("click", openOverlay);
        choiceRow.appendChild(card);
        if (!addrInput.value && pickedClient.address) {
          addrInput.value = pickedClient.address;
          state.address = pickedClient.address;
          saveState();
        }
      } else {
        const empty = el(`
          <div class="picker-empty-state">
            <button class="picker-open-btn" type="button">
              <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7"
                   stroke-linecap="round" stroke-linejoin="round" width="18" height="18" aria-hidden="true">
                <circle cx="12" cy="8" r="4"/><path d="M4 20c0-4 3.6-7 8-7s8 3 8 7"/>
              </svg>
              Выбрать клиента из CRM
            </button>
            <span class="field-error" id="nameError"></span>
          </div>
        `);
        empty.querySelector(".picker-open-btn").addEventListener("click", openOverlay);
        choiceRow.appendChild(empty);
      }
    }

    async function openOverlay() {
      const overlay = el(`
        <div class="client-picker-overlay">
          <div class="picker-sheet">
            <div class="picker-sheet-head">
              <span class="picker-sheet-title">Выбор клиента</span>
              <button class="picker-close-btn" type="button">✕</button>
            </div>
            <div class="picker-search-wrap">
              <input class="picker-search" type="search"
                     placeholder="Поиск по ФИО или телефону…" autocomplete="off">
            </div>
            <div class="picker-list" id="pcList">
              <div class="picker-loading">Загружаем список…</div>
            </div>
          </div>
        </div>
      `);
      document.body.appendChild(overlay);
      requestAnimationFrame(() => overlay.classList.add("open"));

      const listEl   = overlay.querySelector("#pcList");
      const searchEl = overlay.querySelector(".picker-search");

      function closeOverlay() {
        overlay.classList.remove("open");
        setTimeout(() => overlay.remove(), 220);
      }
      overlay.querySelector(".picker-close-btn").addEventListener("click", closeOverlay);
      overlay.addEventListener("click", e => { if (e.target === overlay) closeOverlay(); });

      let clients = [];
      try {
        const res = await fetch(`${BACKEND_URL}/api/clients`, {
          method: "POST",
          body: JSON.stringify({
            initData: tg?.initData || "",
            initDataUnsafe: tg?.initDataUnsafe || null,
          }),
        });
        const data = await res.json();
        clients = (data.clients || []).sort((a, b) =>
          (a.client_name || "").localeCompare(b.client_name || "", "ru")
        );
      } catch (e) {
        listEl.innerHTML = `<div class="picker-error">Ошибка загрузки: ${escHtml(e.message)}</div>`;
        return;
      }

      function renderList(q) {
        q = (q || "").toLowerCase().trim();
        const filtered = q
          ? clients.filter(c =>
              (c.client_name || "").toLowerCase().includes(q) ||
              (c.client_phone || "").replace(/\D/g, "").includes(q.replace(/\D/g, ""))
            )
          : clients;
        listEl.innerHTML = "";
        if (!filtered.length) {
          listEl.innerHTML = `<div class="picker-empty-msg">${q ? "Ничего не найдено" : "Список клиентов пуст"}</div>`;
          return;
        }
        filtered.forEach(c => {
          const row = el(`
            <div class="picker-row">
              <div class="picker-row-name">${escHtml(c.client_name || "—")}</div>
              <div class="picker-row-sub">
                ${c.client_phone ? `<span>${escHtml(c.client_phone)}</span>` : ""}
                ${c.contract_no ? `<span class="picker-contract">дог. ${escHtml(c.contract_no)}</span>` : ""}
              </div>
            </div>
          `);
          row.addEventListener("click", () => {
            pickedClient = {
              client_name:  c.client_name  || "",
              client_phone: c.client_phone || "",
              address:      c.address      || "",
              contract_no:  c.contract_no  || "",
              client_no:    c.client_no    || "",
            };
            closeOverlay();
            refresh();
          });
          listEl.appendChild(row);
        });
      }

      renderList("");
      searchEl.focus();
      searchEl.addEventListener("input", () => renderList(searchEl.value));
    }

    refresh();
    return wrap;
  }

  function bindInputs(node) {
    node.querySelectorAll("[data-bind]").forEach(inp => {
      inp.addEventListener("input", e => {
        state[e.target.dataset.bind] = e.target.value;
        saveState();
      });
    });
  }

  function nextKindSuggestion() {
    // Авто-предложение: сначала Стена 1, потом 2,3,4, затем План, затем Общий
    const usedWalls = new Set(photos.filter(p => p.kind?.startsWith("wall")).map(p => p.kind));
    for (let i = 1; i <= 4; i++) {
      if (!usedWalls.has(`wall${i}`)) return `wall${i}`;
    }
    const hasPlan = photos.some(p => p.kind === "plan");
    if (!hasPlan) return "plan";
    return "general";
  }

  function bindPhotoInput(node) {
    const list = node.querySelector("#photoList");
    const input = node.querySelector("#photoInput");

    function refreshList() {
      list.innerHTML = "";
      if (!photos.length) {
        list.innerHTML = `<div class="empty" style="padding:12px;text-align:center;color:var(--muted);font-size:12px;">Ещё нет фото</div>`;
        return;
      }
      photos.forEach((ph, idx) => {
        const tile = el(`
          <div class="photo-tagged">
            <div class="photo-tagged-thumb">
              <img src="${ph.dataUrl}" alt="фото ${idx + 1}">
              <button class="photo-rm" data-idx="${idx}" aria-label="Удалить">×</button>
            </div>
            <select class="photo-kind" data-idx="${idx}">
              ${PHOTO_KINDS.map(k =>
                `<option value="${k.key}" ${k.key === ph.kind ? "selected" : ""}>${k.label}</option>`
              ).join("")}
            </select>
          </div>
        `);
        tile.querySelector(".photo-rm").addEventListener("click", e => {
          const i = +e.currentTarget.dataset.idx;
          photos.splice(i, 1);
          haptic && haptic("impact");
          refreshList();
        });
        tile.querySelector(".photo-kind").addEventListener("change", e => {
          const i = +e.target.dataset.idx;
          photos[i].kind = e.target.value;
        });
        list.appendChild(tile);
      });
    }

    input.addEventListener("change", async (e) => {
      const files = Array.from(e.target.files || []);
      input.value = "";
      if (!files.length) return;
      for (const f of files) {
        if (photos.length >= 30) break;
        if (!f.type || !f.type.startsWith("image/")) continue;
        try {
          const dataUrl = await compressImage(f, 1800, 0.78);
          const kind = nextKindSuggestion();
          photos.push({ dataUrl, kind });
        } catch (err) {
          console.warn("Не удалось сжать фото", err);
        }
      }
      refreshList();
      haptic && haptic("success");
    });

    refreshList();
  }

  function compressImage(file, maxSide = 1800, quality = 0.78) {
    return new Promise((resolve, reject) => {
      const reader = new FileReader();
      reader.onerror = reject;
      reader.onload = e => {
        const img = new Image();
        img.onerror = reject;
        img.onload = () => {
          let { width, height } = img;
          if (width > maxSide || height > maxSide) {
            if (width >= height) {
              height = Math.round(height * maxSide / width);
              width = maxSide;
            } else {
              width = Math.round(width * maxSide / height);
              height = maxSide;
            }
          }
          const canvas = document.createElement("canvas");
          canvas.width = width;
          canvas.height = height;
          canvas.getContext("2d").drawImage(img, 0, 0, width, height);
          try { resolve(canvas.toDataURL("image/jpeg", quality)); }
          catch (err) { reject(err); }
        };
        img.src = e.target.result;
      };
      reader.readAsDataURL(file);
    });
  }

  /* ===================== Чек-лист — отдельный экран ===================== */

  // Состояние галочек хранится в localStorage по measurement_id (или draft)
  function checklistKey() {
    return `zov-checklist-${measurementId || "draft"}`;
  }
  function loadChecklistState() {
    try { return JSON.parse(localStorage.getItem(checklistKey()) || "{}"); }
    catch (e) { return {}; }
  }
  function saveChecklistState(s) {
    try { localStorage.setItem(checklistKey(), JSON.stringify(s)); } catch (e) {}
  }
  function resetChecklistDraft() {
    try { localStorage.removeItem(`zov-checklist-draft`); } catch (e) {}
  }

  async function renderChecklist() {
    root.innerHTML = "";
    root.appendChild(el(`
      <header class="podbor-header">
        <button class="podbor-back" aria-label="Назад">${ICONS.arrow_left || "‹"}</button>
        <div class="podbor-title">Чек-лист замера</div>
        <button class="podbor-help" id="resetCl" aria-label="Сбросить">↺</button>
      </header>
    `));
    root.querySelector(".podbor-back").addEventListener("click", () => {
      if (measurementId) location.hash = `#/measure?id=${measurementId}`;
      else location.hash = "#/measure";
    });

    const wrap = el(`<section class="podbor-step checklist-page"></section>`);
    root.appendChild(wrap);
    wrap.appendChild(el(`<div class="loader-inline"><div class="spinner"></div></div>`));

    try {
      const res = await fetch("./assets/zamer-checklist.md", { cache: "no-cache" });
      const md = await res.text();
      const clState = loadChecklistState();
      wrap.innerHTML = `
        <div class="checklist-progress" id="clProgress"></div>
        <div class="checklist-md">${renderMarkdown(md, clState)}</div>
      `;
      bindChecklistInteractions(wrap, clState);
      updateChecklistProgress(wrap, clState);

      root.querySelector("#resetCl").addEventListener("click", () => {
        if (!confirm("Сбросить все галочки?")) return;
        const empty = {};
        saveChecklistState(empty);
        renderChecklist();
      });
    } catch (e) {
      wrap.innerHTML = `<div class="error">Не удалось загрузить чек-лист: ${e.message}</div>`;
    }
  }

  function bindChecklistInteractions(wrap, clState) {
    wrap.querySelectorAll(".cl-item").forEach(item => {
      item.addEventListener("click", () => {
        const key = item.dataset.key;
        if (!key) return;
        const isChecked = item.classList.contains("checked");
        const checkSpan = item.querySelector(".cl-check");
        if (isChecked) {
          item.classList.remove("checked");
          if (checkSpan) checkSpan.textContent = "☐";
          delete clState[key];
        } else {
          item.classList.add("checked");
          if (checkSpan) checkSpan.textContent = "☑";
          clState[key] = true;
        }
        saveChecklistState(clState);
        updateChecklistProgress(wrap, clState);
        haptic && haptic("impact");
      });
    });
  }

  function updateChecklistProgress(wrap, clState) {
    const total = wrap.querySelectorAll(".cl-item").length;
    const done = Object.keys(clState).filter(k => clState[k]).length;
    const pct = total ? Math.round((done / total) * 100) : 0;
    const bar = wrap.querySelector("#clProgress");
    if (bar) {
      bar.innerHTML = `
        <div class="cl-pbar"><div class="cl-pbar-fill" style="width:${pct}%"></div></div>
        <div class="cl-pcount">${done} из ${total} · ${pct}%</div>
      `;
    }
  }

  /* Минимальный markdown → HTML: заголовки, списки, таблицы, code, чекбоксы */
  function renderMarkdown(md, clState = {}) {
    const lines = md.split("\n");
    const out = [];
    let inList = false;
    let inTable = false;
    let tableRows = [];

    function closeList() { if (inList) { out.push("</ul>"); inList = false; } }
    function closeTable() {
      if (!inTable) return;
      if (tableRows.length) {
        const html = ["<table class='cl-table'>"];
        tableRows.forEach((cells, i) => {
          const tag = i === 0 ? "th" : "td";
          if (i === 1 && cells.every(c => /^[-:\s|]+$/.test(c))) return; // skip separator
          html.push(`<tr>${cells.map(c => `<${tag}>${inline(c)}</${tag}>`).join("")}</tr>`);
        });
        html.push("</table>");
        out.push(html.join(""));
      }
      tableRows = [];
      inTable = false;
    }

    function inline(s) {
      return escHtml(s)
        .replace(/`([^`]+)`/g, "<code>$1</code>")
        .replace(/\*\*([^*]+)\*\*/g, "<strong>$1</strong>")
        .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>');
    }

    for (const raw of lines) {
      const line = raw.trimEnd();
      // Директива @pict:KEY — вставляем SVG-эскиз из ZAMER_PICTS
      const pictMatch = line.match(/^@pict:([a-z_]+)$/i);
      if (pictMatch) {
        closeList();
        closeTable();
        const key = pictMatch[1].toLowerCase();
        const svg = (window.ZAMER_PICTS || {})[key];
        if (svg) {
          out.push(`<div class="cl-pict">${svg}</div>`);
        }
        continue;
      }
      // Таблица
      if (line.includes("|") && line.match(/^\s*\|/)) {
        if (!inTable) { closeList(); inTable = true; }
        const cells = line.split("|").slice(1, -1).map(s => s.trim());
        tableRows.push(cells);
        continue;
      } else if (inTable) {
        closeTable();
      }
      // Заголовки
      if (line.startsWith("# ")) {
        closeList();
        out.push(`<h1>${inline(line.slice(2))}</h1>`);
      } else if (line.startsWith("## ")) {
        closeList();
        out.push(`<h2>${inline(line.slice(3))}</h2>`);
      } else if (line.startsWith("### ")) {
        closeList();
        out.push(`<h3>${inline(line.slice(4))}</h3>`);
      } else if (line.startsWith("- ") || line.startsWith("* ")) {
        if (!inList) { out.push("<ul>"); inList = true; }
        let content = line.slice(2);
        // [ ] checkbox — делаем интерактивным с уникальным ключом
        if (content.startsWith("[ ] ") || content.startsWith("[x] ") || content.startsWith("[X] ")) {
          const text = content.slice(4);
          // Ключ = первые 60 символов содержимого (для стабильности при edit)
          const key = "cl_" + text.replace(/[^\wа-яА-ЯёЁ]+/g, "_").slice(0, 60).toLowerCase();
          const checked = !!clState[key];
          out.push(
            `<li class="cl-item${checked ? " checked" : ""}" data-key="${key}">` +
              `<span class="cl-check">${checked ? "☑" : "☐"}</span> ${inline(text)}` +
            `</li>`
          );
        } else {
          out.push(`<li>${inline(content)}</li>`);
        }
      } else if (line === "---") {
        closeList();
        out.push(`<hr>`);
      } else if (line === "") {
        closeList();
        out.push("");
      } else {
        closeList();
        out.push(`<p>${inline(line)}</p>`);
      }
    }
    closeList();
    closeTable();
    return out.join("\n");
  }

  /* ===================== Submit ===================== */

  async function onSubmit(node) {
    const btn = node.querySelector("#submitBtn");
    const result = node.querySelector("#submitResult");
    btn.disabled = true;
    btn.innerHTML = '<span class="spinner-inline"></span> сохраняем...';
    result.innerHTML = "";

    const isUpdate = !!measurementId && prefilledClient;
    if (!isUpdate) {
      if (!pickedClient) {
        const nameErr = node.querySelector("#nameError");
        if (nameErr) {
          nameErr.textContent = "Выберите клиента из списка";
          nameErr.scrollIntoView({ behavior: "smooth", block: "center" });
        }
        btn.disabled = false; btn.textContent = "Сохранить замер";
        return;
      }
    }
    if (!photos.length) {
      result.innerHTML = `<div class="error">Добавьте хотя бы одно фото замера.</div>`;
      btn.disabled = false; btn.textContent = isUpdate ? "Закрыть заявку" : "Сохранить замер";
      return;
    }

    const measurement = {
      // Структурированные фото + их типы
      photos: photos.map(p => p.dataUrl),
      photos_meta: photos.map(p => ({ kind: p.kind })),
      // Общая инфа замера
      zamer_no: state.zamer_no || "",
      zamer_date: state.zamer_date || "",
      notes: state.notes || "",
      // Клиент
      client_name:  isUpdate ? prefilledClient.name  : pickedClient.client_name,
      client_phone: isUpdate ? prefilledClient.phone : pickedClient.client_phone,
      address:      isUpdate ? prefilledClient.address : state.address,
      measurement_id: measurementId || undefined,
    };

    try {
      const res = await fetch(`${BACKEND_URL}/api/measurement`, {
        method: "POST",
        body: JSON.stringify({
          initData: tg?.initData || "",
          initDataUnsafe: tg?.initDataUnsafe || null,
          measurement,
        }),
      });
      const data = await res.json();
      if (data.error) {
        result.innerHTML = `<div class="error">Ошибка: ${data.error}</div>`;
        btn.disabled = false; btn.textContent = isUpdate ? "Закрыть заявку" : "Сохранить замер";
        return;
      }
      haptic && haptic("success");
      result.innerHTML = `
        <div class="success">
          <div class="success-icon">${ICONS.check}</div>
          <div>
            <div class="success-title">${isUpdate ? "Заявка закрыта" : "Замер сохранён"}</div>
            <div class="success-sub">${photos.length} фото · ID #${(data.id || "").slice(0, 6)}</div>
          </div>
        </div>
        <div class="podbor-cta-row" style="margin-top:16px;">
          <button class="btn-secondary" id="newOne">Ещё замер</button>
          <button class="btn-primary" id="toHome">На главную</button>
        </div>
      `;
      reset();
      node.querySelector("#newOne")?.addEventListener("click", () => mount(root));
      node.querySelector("#toHome")?.addEventListener("click", () => {
        location.hash = "";
        location.reload();
      });
    } catch (e) {
      result.innerHTML = `<div class="error">Сеть: ${e.message}</div>`;
      btn.disabled = false; btn.textContent = isUpdate ? "Закрыть заявку" : "Сохранить замер";
    }
  }

  /* ===================== Helpers ===================== */

  function escHtml(s) {
    return String(s == null ? "" : s)
      .replace(/&/g, "&amp;").replace(/</g, "&lt;")
      .replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  }
  function escAttr(s) { return escHtml(s); }

  return { mount, reset, kindLabel, PHOTO_KINDS };
})();
