(function (global) {
  "use strict";

  const DEFAULT_OPTIMIZE_KEYS = [
    "dry_matter",
    "usp",
    "nel",
    "crude_protein",
    "rnb",
    "nfc_pct_dm_uk",
  ];

  let catalog = [];
  let groups = [];
  let profiles = [];
  let currentStep = 1;
  const selections = {};

  function formulateError(message) {
    global.WespLabHeisenbergPrime?.error?.(message, "formulate");
  }

  async function api(path, opts) {
    const resp = await fetch(path, opts);
    const data = await resp.json().catch(() => ({}));
    if (!resp.ok) throw new Error(data.message || `Наука, чёрт возьми — ошибка ${resp.status}`);
    return data;
  }

  function escapeHtml(s) {
    return String(s ?? "")
      .replace(/&/g, "&amp;")
      .replace(/</g, "&lt;")
      .replace(/>/g, "&gt;")
      .replace(/"/g, "&quot;");
  }

  function formatNum(v) {
    if (v == null || v === "") return "—";
    const n = Number(v);
    if (Number.isNaN(n)) return "—";
    return Math.abs(n) >= 100 ? n.toFixed(1) : n.toFixed(3).replace(/\.?0+$/, "");
  }

  function diffClass(diff) {
    if (diff == null || Number.isNaN(Number(diff))) return "lab-sandbox-diff--na";
    const d = Number(diff);
    if (Math.abs(d) < 0.001) return "lab-sandbox-diff--ok";
    return "lab-sandbox-diff--bad";
  }

  function totalSteps() {
    return groups.length + 1;
  }

  function groupById(id) {
    return groups.find((g) => g.id === id);
  }

  function selectedForGroup(gid) {
    return Array.from(document.querySelectorAll(`.lab-formulate-gcb[data-group="${gid}"]:checked`)).map(
      (el) => el.value
    );
  }

  function allSelections() {
    const out = {};
    groups.forEach((g) => {
      out[g.id] = selectedForGroup(g.id);
    });
    return out;
  }

  function totalPicked() {
    return Object.values(allSelections()).reduce((n, arr) => n + arr.length, 0);
  }

  function componentsForGroup(gid, filterText) {
    const q = (filterText || "").trim().toLowerCase();
    return catalog.filter((c) => {
      if (!c.eligible || c.feedGroup !== gid) return false;
      if (q && !String(c.name || "").toLowerCase().includes(q)) return false;
      return true;
    });
  }

  function renderGroupTable(gid, filterText) {
    const body = document.querySelector(`#labFormulatePane-${gid} tbody`);
    if (!body) return;
    const picked = new Set(selectedForGroup(gid));
    const rows = componentsForGroup(gid, filterText);
    if (!rows.length) {
      body.innerHTML =
        '<tr><td colspan="4" class="lab-sandbox-empty">В этой фракции пусто — нечего готовить</td></tr>';
      return;
    }
    body.innerHTML = rows
      .map((c) => {
        const checked = picked.has(c.id) ? " checked" : "";
        return (
          `<tr>` +
          `<td><input type="checkbox" class="form-check-input lab-formulate-gcb" data-group="${escapeHtml(gid)}" value="${escapeHtml(c.id)}"${checked}></td>` +
          `<td>${escapeHtml(c.name)}</td>` +
          `<td>${formatNum(c.mainFeedDmGPerKg)}</td>` +
          `<td>${formatNum(c.dryMatterPct)}%</td>` +
          `</tr>`
        );
      })
      .join("");
  }

  function buildWizard() {
    const stepsEl = document.getElementById("labFormulateSteps");
    const panesEl = document.getElementById("labFormulatePanes");
    if (!stepsEl || !panesEl) return;

    stepsEl.innerHTML =
      groups
        .map(
          (g, i) =>
            `<button type="button" class="lab-formulate-step${i === 0 ? " lab-formulate-step--active" : ""}" data-step="${i + 1}">${i + 1}. ${escapeHtml(g.shortLabel)}</button>`
        )
        .join("") +
      `<button type="button" class="lab-formulate-step" data-step="${groups.length + 1}">${groups.length + 1}. Кристалл</button>`;

    panesEl.innerHTML = groups
      .map((g) => {
        const req = g.required
          ? `<span class="lab-formulate-badge lab-formulate-badge--ok">без этого никак</span>`
          : `<span class="lab-formulate-badge">не мешай</span>`;
        return (
          `<div id="labFormulatePane-${g.id}" class="lab-formulate-pane${g.step === 1 ? "" : " d-none"}" data-group="${g.id}">` +
          `<div class="d-flex justify-content-between align-items-center mb-2">` +
          `<div class="lab-sandbox-panel__title mb-0">${escapeHtml(g.label)}</div>${req}</div>` +
          `<p class="small text-muted mb-2">${escapeHtml(g.hint)}</p>` +
          `<p class="small mb-2 lab-formulate-group-count" data-group="${g.id}">В партии: 0</p>` +
          `<div class="lab-sandbox-field">` +
          `<input type="search" class="form-control form-control-sm lab-formulate-filter" data-group="${g.id}" placeholder="Найти реагент">` +
          `</div>` +
          `<div class="table-responsive lab-formulate-pool-wrap">` +
          `<table class="table table-sm lab-sandbox-table mb-0">` +
          `<thead><tr><th></th><th>Реагент</th><th>Осн.</th><th>СВ%</th></tr></thead>` +
          `<tbody></tbody></table></div>` +
          `<div class="lab-sandbox-actions mt-2">` +
          (g.step > 1
            ? `<button type="button" class="btn btn-sm btn-outline-secondary lab-formulate-back" data-step="${g.step}">← Откат</button>`
            : "") +
          (!g.required
            ? `<button type="button" class="btn btn-sm btn-outline-secondary lab-formulate-skip" data-step="${g.step}">Не мешай</button>`
            : "") +
          `<button type="button" class="btn btn-sm btn-primary lab-formulate-next" data-step="${g.step}">Вперёд →</button>` +
          `</div></div>`
        );
      })
      .join("");

    groups.forEach((g) => renderGroupTable(g.id, ""));
  }

  function updateCounters() {
    const sel = allSelections();
    groups.forEach((g) => {
      const el = document.querySelector(`.lab-formulate-group-count[data-group="${g.id}"]`);
      if (el) {
        const n = (sel[g.id] || []).length;
        el.textContent = g.required ? `В партии: ${n} (мин. ${g.minPick})` : `В партии: ${n}`;
      }
    });
    const total = totalPicked();
    const summaryEl = document.getElementById("labFormulateSummary");
    const prefilter = document.getElementById("labFormulatePrefilterNote");
    const roughOk = (sel.rough || []).length >= 1;
    if (summaryEl) {
      summaryEl.textContent = roughOk && total >= 3
        ? `Партия из ${total} реагентов — можно готовить`
        : `Нужна база (≥1) и ≥3 реагента всего — сейчас ${total}. Нам нужно готовить.`;
    }
    if (prefilter) prefilter.classList.toggle("d-none", total <= 18);
    const runBtn = document.getElementById("labFormulateRun");
    if (runBtn) runBtn.disabled = !(roughOk && total >= 3 && currentStep === totalSteps());
    renderReview();
  }

  function renderReview() {
    const el = document.getElementById("labFormulateReview");
    if (!el) return;
    const sel = allSelections();
    let html = "";
    groups.forEach((g) => {
      const ids = sel[g.id] || [];
      html += `<li><strong>${escapeHtml(g.shortLabel)}</strong> (${ids.length})</li>`;
      ids.forEach((id) => {
        const name = catalog.find((c) => c.id === id)?.name || id;
        html += `<li class="ms-3">${escapeHtml(name)}</li>`;
      });
    });
    el.innerHTML = html || "<li>—</li>";
  }

  function setStep(step) {
    currentStep = step;
    const calcStep = totalSteps();
    groups.forEach((g) => {
      document.getElementById(`labFormulatePane-${g.id}`)?.classList.toggle("d-none", g.step !== step);
    });
    document.getElementById("labFormulateReviewPane")?.classList.toggle("d-none", step !== calcStep);
    document.querySelector(".lab-formulate-step-panel--calc")?.classList.toggle("d-none", step !== calcStep);
    document.querySelectorAll(".lab-formulate-step").forEach((btn) => {
      btn.classList.toggle("lab-formulate-step--active", Number(btn.dataset.step) === step);
    });
    updateCounters();
  }

  function canGoToStep(step) {
    if (step <= 1) return true;
    const sel = allSelections();
    for (const g of groups) {
      if (g.step >= step) break;
      if (g.required && (sel[g.id] || []).length < (g.minPick || 1)) return false;
    }
    if (step === totalSteps()) {
      return (sel.rough || []).length >= 1 && totalPicked() >= 3;
    }
    return true;
  }

  function readNumInput(id) {
    const el = document.getElementById(id);
    if (!el) return null;
    const raw = String(el.value ?? "").trim();
    if (!raw) return null;
    const n = Number(raw);
    return Number.isFinite(n) ? n : null;
  }

  function readNormsMethod() {
    return document.getElementById("labFormulateNormsMethod")?.value || "wesp";
  }

  function readNormsParams() {
    const method = readNormsMethod();
    if (method === "wesp") return {};
    const params = {
      milkFatPct: readNumInput("labFormulateFat") ?? 4,
      lactationNo: readNumInput("labFormulateLactation") ?? 2,
    };
    if (method === "racion_piter") {
      params.koncOeSv = readNumInput("labFormulateKonc") ?? 10.3;
    }
    return params;
  }

  function syncNormsMethodUi() {
    const method = readNormsMethod();
    const racionBox = document.getElementById("labFormulateRacionFields");
    const piterOnly = document.querySelectorAll(".lab-formulate-piter-only");
    if (racionBox) racionBox.classList.toggle("d-none", method === "wesp");
    piterOnly.forEach((el) => el.classList.toggle("d-none", method !== "racion_piter"));
  }

  function readFormulateParams() {
    return {
      profileId: document.getElementById("labFormulateProfile")?.value || "",
      massKg: readNumInput("labFormulateMass"),
      milkYieldKg: readNumInput("labFormulateMilk"),
      totalKgPerHead: readNumInput("labFormulateTotalKg") ?? 7.3,
      costWeight: readNumInput("labFormulateCostWeight") ?? 100,
      optimizeKeys: Array.from(document.querySelectorAll(".lab-formulate-opt-cb:checked")).map((el) => el.value),
      groupSelections: allSelections(),
      normsMethod: readNormsMethod(),
      normsParams: readNormsParams(),
    };
  }

  function resolveMassMilk(profile) {
    return {
      mass: readNumInput("labFormulateMass") ?? profile?.massKg ?? null,
      milk: readNumInput("labFormulateMilk") ?? profile?.milkYieldKg ?? null,
    };
  }

  async function normsForOptimizeDisplay(profile) {
    const method = readNormsMethod();
    let norms =
      method === "wesp"
        ? { ...(profile?.norms || profile?.normsData?.indicators || {}) }
        : {};
    const { mass, milk } = resolveMassMilk(profile);
    if (method !== "wesp" && (mass == null || milk == null)) return norms;
    if (mass == null && milk == null) return norms;
    try {
      const q = new URLSearchParams({ method });
      const profileId = document.getElementById("labFormulateProfile")?.value;
      if (profileId) q.set("profile_id", profileId);
      if (mass != null) q.set("mass_kg", String(mass));
      if (milk != null) q.set("milk_yield_kg", String(milk));
      const np = readNormsParams();
      if (np.milkFatPct != null) q.set("milk_fat_pct", String(np.milkFatPct));
      if (np.lactationNo != null) q.set("lactation_no", String(np.lactationNo));
      if (np.koncOeSv != null) q.set("konc_oe_sv", String(np.koncOeSv));
      const preview = await api(`/api/lab/norms-preview?${q.toString()}`);
      const resolved = preview.resolvedIndicators || {};
      const dynamic = preview.dynamicNorms || {};
      const merged = { ...resolved };
      Object.keys(dynamic).forEach((key) => {
        const d = dynamic[key];
        if (d?.min != null) merged[key] = { ...(merged[key] || {}), min: d.min };
      });
      if (method === "wesp") {
        Object.assign(norms, merged);
      } else {
        norms = merged;
      }
    } catch (_err) {
      /* preview optional */
    }
    return norms;
  }

  async function refreshOptimizeKeysForInputs() {
    const profileId = document.getElementById("labFormulateProfile")?.value;
    const profile = profiles.find((p) => String(p.id) === String(profileId));
    if (!profile) return;
    const norms = await normsForOptimizeDisplay(profile);
    renderOptimizeKeys({ ...profile, norms });
  }

  let optimizeKeysRefreshTimer = null;
  function scheduleOptimizeKeysRefresh() {
    if (optimizeKeysRefreshTimer) clearTimeout(optimizeKeysRefreshTimer);
    optimizeKeysRefreshTimer = setTimeout(() => {
      optimizeKeysRefreshTimer = null;
      refreshOptimizeKeysForInputs().catch(() => {});
    }, 200);
  }

  function profileOptions(selectedId) {
    const opts = ['<option value="">— стандарт чистоты —</option>'];
    profiles.forEach((p) => {
      const sel = String(p.id) === String(selectedId) ? " selected" : "";
      const key = p.profileKey ? `[${p.profileKey}] ` : "";
      opts.push(
        `<option value="${escapeHtml(p.id)}"${sel}>${escapeHtml(key)}${escapeHtml(p.label)}</option>`
      );
    });
    return opts.join("");
  }

  function fillProfileFields(profileId) {
    const profile = profiles.find((p) => String(p.id) === String(profileId));
    if (!profile) return;
    const massEl = document.getElementById("labFormulateMass");
    const milkEl = document.getElementById("labFormulateMilk");
    if (massEl && profile.massKg != null) massEl.value = profile.massKg;
    if (milkEl && profile.milkYieldKg != null) milkEl.value = profile.milkYieldKg;
    refreshOptimizeKeysForInputs().catch(() => renderOptimizeKeys(profile));
  }

  function renderOptimizeKeys(profile) {
    const box = document.getElementById("labFormulateOptimizeKeys");
    if (!box) return;
    const norms = profile?.norms || profile?.normsData?.indicators || {};
    const keys = Object.keys(norms).filter((k) => norms[k]?.min != null || norms[k]?.max != null);
    const useKeys = keys.length ? keys : DEFAULT_OPTIMIZE_KEYS;
    const prevChecked = new Set(
      Array.from(document.querySelectorAll(".lab-formulate-opt-cb:checked")).map((el) => el.value)
    );
    const hasPrev = prevChecked.size > 0;
    box.innerHTML = useKeys
      .map((key) => {
        const checked =
          (hasPrev ? prevChecked.has(key) : DEFAULT_OPTIMIZE_KEYS.includes(key)) ? " checked" : "";
        const b = norms[key] || {};
        const bounds =
          b.min != null && b.max != null
            ? `${formatNum(b.min)}–${formatNum(b.max)}`
            : b.max != null
              ? `≤ ${formatNum(b.max)}`
              : b.min != null
                ? `≥ ${formatNum(b.min)}`
                : "";
        return (
          `<label class="lab-formulate-opt-key">` +
          `<input type="checkbox" class="form-check-input lab-formulate-opt-cb" value="${escapeHtml(key)}"${checked}>` +
          `<span>${escapeHtml(key)}</span>` +
          `<small class="text-muted">${escapeHtml(bounds)}</small>` +
          `</label>`
        );
      })
      .join("");
  }

  function renderResult(data) {
    const el = document.getElementById("labFormulateResult");
    if (!el) return;
    if (!data) {
      el.innerHTML = '<div class="lab-sandbox-empty">Скажи моё имя и жми кнопку</div>';
      return;
    }
    const stats = data.searchStats || {};
    const optimizeSet = new Set(data.optimizeKeys || []);
    let html =
      `<div class="lab-formulate-result-meta">` +
      `<span>Реагентов ${data.candidatePoolSize}</span>` +
      `<span>Троек ${stats.tripletsEvaluated || 0}</span>` +
      `<span>${stats.durationMs || 0} мс — время деньги</span></div>` +
      `<table class="table table-sm lab-sandbox-table mb-3"><thead><tr><th>Реагент</th><th>кг/сут</th><th>%</th></tr></thead><tbody>` +
      (data.lines || [])
        .map(
          (line) =>
            `<tr><td>${escapeHtml(line.name)}</td><td>${formatNum(line.dailyKg)}</td><td>${formatNum(line.sharePct)}</td></tr>`
        )
        .join("") +
      `</tbody></table>`;
    const rows = (data.indicators || []).filter(
      (ind) => ind.min != null || ind.max != null || ind.content != null
    );
    html +=
      `<table class="table table-sm lab-sandbox-table"><thead><tr><th>Показатель</th><th>Факт</th><th>Стандарт</th><th>Δ</th></tr></thead><tbody>` +
      rows
        .map((ind) => {
          const target = optimizeSet.has(ind.key) ? " lab-formulate-row--target" : "";
          const dc = ind.min != null || ind.max != null ? diffClass(ind.diff) : "lab-sandbox-diff--na";
          const norm =
            ind.min != null && ind.max != null
              ? `${formatNum(ind.min)}–${formatNum(ind.max)}`
              : ind.max != null
                ? `≤ ${formatNum(ind.max)}`
                : ind.min != null
                  ? `≥ ${formatNum(ind.min)}`
                  : "—";
          return (
            `<tr class="${target}"><td>${escapeHtml(ind.label || ind.key)}</td>` +
            `<td>${formatNum(ind.content)}</td><td>${norm}</td>` +
            `<td class="${dc}">${formatNum(ind.diff)}</td></tr>`
          );
        })
        .join("") +
      `</tbody></table>`;
    el.innerHTML = html;
  }

  async function loadCatalogs() {
    const [compData, profData] = await Promise.all([
      api("/api/lab/formulate/components"),
      api("/api/lab/animal-profiles?ration_type=DAIRY"),
    ]);
    catalog = compData.components || [];
    groups = (compData.groups || []).slice().sort((a, b) => a.step - b.step);
    profiles = profData.profiles || [];
    buildWizard();
    const profileSel = document.getElementById("labFormulateProfile");
    if (profileSel) {
      const labDefault = profiles.find((p) => p.profileKey === "lab_math_dairy_01") || profiles[0];
      profileSel.innerHTML = profileOptions(labDefault?.id);
      if (labDefault) fillProfileFields(labDefault.id);
    }
    setStep(1);
  }

  async function labFormulateRun() {
    const params = readFormulateParams();
    const profile = profiles.find((p) => String(p.id) === String(params.profileId));
    if (!params.profileId) throw new Error("Выбери стандарт чистоты — я предупреждал");
    if (params.normsMethod !== "wesp") {
      const { mass, milk } = resolveMassMilk(profile);
      if (mass == null || milk == null) {
        throw new Error("Для Москва/Петербург нужны масса и удой — заполни поля или выбери профиль с удоем");
      }
      params.massKg = mass;
      params.milkYieldKg = milk;
    }
    if ((params.groupSelections.rough || []).length < 1) {
      throw new Error("Без базы не готовим. Минимум один грубый корм");
    }
    if (totalPicked() < 3) throw new Error("Минимум три реагента. Нам нужно готовить");
    const data = await api("/api/lab/formulate", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      cache: "no-store",
      body: JSON.stringify({
        profileId: params.profileId,
        groupSelections: params.groupSelections,
        massKg: params.massKg,
        milkYieldKg: params.milkYieldKg,
        totalKgPerHead: params.totalKgPerHead,
        costWeight: params.costWeight,
        optimizeKeys: params.optimizeKeys,
        normsMethod: params.normsMethod,
        normsParams: params.normsParams,
      }),
    });
    renderResult(data);
    return data;
  }

  function selectLabForGroup(gid) {
    catalog
      .filter((c) => c.eligible && c.feedGroup === gid && String(c.name || "").startsWith("LAB тест —"))
      .forEach((c) => {
        const cb = document.querySelector(
          `.lab-formulate-gcb[data-group="${CSS.escape(gid)}"][value="${CSS.escape(c.id)}"]`
        );
        if (cb) cb.checked = true;
      });
    updateCounters();
  }

  function bindEvents() {
    document.getElementById("labFormulateRun")?.addEventListener("click", async () => {
      try {
        await labFormulateRun();
      } catch (err) {
        formulateError(String(err.message || err));
      } finally {
        updateCounters();
      }
    });

    document.getElementById("labFormulateProfile")?.addEventListener("change", (e) => {
      fillProfileFields(e.target.value);
    });

    for (const id of [
      "labFormulateMass",
      "labFormulateMilk",
      "labFormulateTotalKg",
      "labFormulateCostWeight",
      "labFormulateFat",
      "labFormulateLactation",
      "labFormulateKonc",
    ]) {
      document.getElementById(id)?.addEventListener("input", scheduleOptimizeKeysRefresh);
    }
    document.getElementById("labFormulateNormsMethod")?.addEventListener("change", () => {
      syncNormsMethodUi();
      scheduleOptimizeKeysRefresh();
    });

    document.getElementById("labFormulatePanes")?.addEventListener("input", (e) => {
      if (e.target.classList.contains("lab-formulate-filter")) {
        renderGroupTable(e.target.dataset.group, e.target.value);
      }
    });

    document.getElementById("labFormulatePanes")?.addEventListener("change", (e) => {
      if (e.target.classList.contains("lab-formulate-gcb")) updateCounters();
    });

    document.getElementById("labFormulatePanes")?.addEventListener("click", (e) => {
      const next = e.target.closest(".lab-formulate-next");
      const back = e.target.closest(".lab-formulate-back");
      const skip = e.target.closest(".lab-formulate-skip");
      if (next) {
        const step = Number(next.dataset.step);
        const g = groups.find((x) => x.step === step);
        if (g?.required && selectedForGroup(g.id).length < (g.minPick || 1)) {
          formulateError(`В «${g.label}» нужно минимум ${g.minPick}. Всё под контролем — выбери`);
          return;
        }
        setStep(step + 1);
      }
      if (back) setStep(Number(back.dataset.step) - 1);
      if (skip) setStep(Number(skip.dataset.step) + 1);
    });

    document.getElementById("labFormulateSteps")?.addEventListener("click", (e) => {
      const btn = e.target.closest(".lab-formulate-step");
      if (!btn) return;
      const step = Number(btn.dataset.step);
      if (!canGoToStep(step)) return;
      setStep(step);
    });

    document.addEventListener("keydown", (e) => {
      if (e.altKey && e.key === "l") {
        selectLabForGroup("rough");
        selectLabForGroup("succulent");
        selectLabForGroup("concentrate");
      }
    });
  }

  function init() {
    bindEvents();
    syncNormsMethodUi();
    loadCatalogs().catch((err) => {
      const msg = err.message || String(err);
      formulateError(msg);
      const panes = document.getElementById("labFormulatePanes");
      if (panes) panes.innerHTML = `<p class="text-danger">${escapeHtml(msg)}</p>`;
    });
  }

  global.labFormulateRun = labFormulateRun;
  if (document.readyState === "loading") {
    document.addEventListener("DOMContentLoaded", init);
  } else {
    init();
  }
})(window);
