// dashboard-who-step-form-phase-2-data-drive.js — extracted verbatim from dashboard.js (pure move).

// ── WHO step form (Phase 2 — data-driven from /source-catalog) ─────

// Catalog cache so we don't refetch on every wizard mount
let _composerSourceCatalog = null;
let _composerSourceFamilies = null;

async function _ensureSourceCatalog() {
  if (_composerSourceCatalog)
    return { catalog: _composerSourceCatalog, families: _composerSourceFamilies };
  const r = await api("composition-source-catalog").catch(() => null);
  if (r && Array.isArray(r.catalog)) {
    _composerSourceCatalog = r.catalog;
    _composerSourceFamilies = r.families || [];
  }
  return { catalog: _composerSourceCatalog || [], families: _composerSourceFamilies || [] };
}

function _composerWhoForm(draft, onChange) {
  const root = el("div", {});

  const recount = el(
    "button",
    {
      class: "btn btn-tiny btn-secondary composer-recount",
      type: "button",
      style: "margin-left: 8px;",
      onclick: () => onChange?.(),
    },
    "↻ Refresh count",
  );

  const combineSelect = el("select", { class: "wizard-input", style: "max-width: 130px;" }, [
    el("option", { value: "union" }, "Union (any)"),
    el("option", { value: "intersect" }, "Intersect (all)"),
    el("option", { value: "exclude" }, "Exclude others"),
  ]);
  combineSelect.value = draft.audience.combine || "union";
  combineSelect.addEventListener("change", () => {
    draft.audience.combine = combineSelect.value;
    onChange?.();
  });

  const exclWindow = el("input", {
    type: "number",
    class: "wizard-input",
    value: draft.audience.exclude_already_contacted_within_days ?? 30,
    min: "0",
    style: "width: 70px;",
  });
  exclWindow.addEventListener("change", () => {
    draft.audience.exclude_already_contacted_within_days = parseInt(exclWindow.value, 10) || 0;
    onChange?.();
  });

  const resolverList = el("div", { style: "display: flex; flex-direction: column; gap: 8px;" });

  const renderResolvers = () => {
    resolverList.innerHTML = "";
    if (draft.audience.resolvers.length === 0) {
      resolverList.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size: 12px; padding: 8px;" },
          "No audience yet. Add a source below.",
        ),
      );
      return;
    }
    draft.audience.resolvers.forEach((r, i) => {
      resolverList.appendChild(
        _resolverCard(r, i, draft, () => {
          renderResolvers();
          onChange?.();
        }),
      );
    });
  };

  const addBtn = el(
    "button",
    {
      class: "btn btn-secondary",
      type: "button",
      onclick: () =>
        _openSourcePickerModal((picked) => {
          draft.audience.resolvers.push(_seedResolverFromCatalog(picked));
          renderResolvers();
          onChange?.();
        }),
    },
    "+ Add audience source",
  );

  const combineRow = el(
    "div",
    { class: "row", style: "gap: 8px; margin-top: 8px; align-items: center; font-size: 12px;" },
    [
      "Combine multiple:",
      combineSelect,
      helpIcon("combine_mode"),
      el("span", { class: "muted", style: "margin-left: 16px;" }, "Skip already-contacted within"),
      exclWindow,
      "days",
      helpIcon("recency_days"),
    ],
  );

  const addBar = el(
    "div",
    { class: "row", style: "gap: 8px; margin-top: 12px; flex-wrap: wrap;" },
    [addBtn, recount],
  );

  // Phase 33 (over Phase 25 backend): Advanced audience knobs expander —
  // surfaces audience.max_per_run, audience.require_fields, and the
  // trigger.working_hours / max_per_hour controls. Collapsed by default
  // so beginner UX stays clean.
  const advancedToggle = el(
    "button",
    {
      class: "btn btn-tiny btn-secondary",
      type: "button",
      style: "margin-top: 14px;",
    },
    "▶ Advanced controls",
  );
  const advancedPanel = el("div", {
    style:
      "display: none; margin-top: 10px; padding: 12px; background: var(--bg-0); border: 1px solid var(--border); border-radius: 6px;",
  });
  advancedToggle.onclick = () => {
    const isOpen = advancedPanel.style.display !== "none";
    advancedPanel.style.display = isOpen ? "none" : "block";
    advancedToggle.textContent = isOpen ? "▶ Advanced controls" : "▼ Advanced controls";
  };

  // 1. audience.max_per_run
  const maxPerRunInput = el("input", {
    type: "number",
    class: "wizard-input",
    min: "0",
    value: String(draft.audience.max_per_run ?? ""),
    placeholder: "no cap",
    style: "width: 90px;",
  });
  maxPerRunInput.addEventListener("change", () => {
    const n = parseInt(maxPerRunInput.value, 10);
    if (Number.isFinite(n) && n > 0) draft.audience.max_per_run = n;
    else delete draft.audience.max_per_run;
    onChange?.();
  });

  // 2. audience.require_fields (multi-tag input — comma-separated)
  const requireInput = el("input", {
    type: "text",
    class: "wizard-input",
    value: Array.isArray(draft.audience.require_fields)
      ? draft.audience.require_fields.join(", ")
      : "",
    placeholder: "e.g. email, linkedin_url",
    style: "width: 240px;",
  });
  requireInput.addEventListener("change", () => {
    const list = requireInput.value
      .split(",")
      .map((s) => s.trim())
      .filter(Boolean);
    if (list.length > 0) draft.audience.require_fields = list;
    else delete draft.audience.require_fields;
    onChange?.();
  });

  // 3. trigger.working_hours (only meaningful for schedule/event triggers)
  if (!draft.trigger) draft.trigger = { type: "manual" };
  const wh = draft.trigger.working_hours || {};
  const whTzInput = el("input", {
    type: "text",
    class: "wizard-input",
    value: wh.tz || "",
    placeholder: "UTC, America/New_York, …",
    style: "width: 200px;",
  });
  const whStartInput = el("input", {
    type: "number",
    class: "wizard-input",
    min: "0",
    max: "23",
    value: wh.start_hour ?? "",
    placeholder: "9",
    style: "width: 60px;",
  });
  const whEndInput = el("input", {
    type: "number",
    class: "wizard-input",
    min: "0",
    max: "23",
    value: wh.end_hour ?? "",
    placeholder: "18",
    style: "width: 60px;",
  });
  const whWeekdaysOnly = el("input", {
    type: "checkbox",
    checked: wh.weekdays_only ? "checked" : null,
  });
  const updateWh = () => {
    const tz = whTzInput.value.trim();
    const sh = parseInt(whStartInput.value, 10);
    const eh = parseInt(whEndInput.value, 10);
    const isOn = tz || Number.isFinite(sh) || Number.isFinite(eh) || whWeekdaysOnly.checked;
    if (!isOn) {
      delete draft.trigger.working_hours;
      return;
    }
    draft.trigger.working_hours = {};
    if (tz) draft.trigger.working_hours.tz = tz;
    if (Number.isFinite(sh)) draft.trigger.working_hours.start_hour = sh;
    if (Number.isFinite(eh)) draft.trigger.working_hours.end_hour = eh;
    if (whWeekdaysOnly.checked) draft.trigger.working_hours.weekdays_only = true;
  };
  whTzInput.addEventListener("change", updateWh);
  whStartInput.addEventListener("change", updateWh);
  whEndInput.addEventListener("change", updateWh);
  whWeekdaysOnly.addEventListener("change", updateWh);

  // 4. trigger.max_per_hour (rate limit for event triggers)
  const maxPerHourInput = el("input", {
    type: "number",
    class: "wizard-input",
    min: "0",
    value: String(draft.trigger.max_per_hour ?? ""),
    placeholder: "no limit",
    style: "width: 70px;",
  });
  maxPerHourInput.addEventListener("change", () => {
    const n = parseInt(maxPerHourInput.value, 10);
    if (Number.isFinite(n) && n > 0) draft.trigger.max_per_hour = n;
    else delete draft.trigger.max_per_hour;
  });

  // 5. trigger.cooldown_after_run_minutes (debounce)
  const cooldownInput = el("input", {
    type: "number",
    class: "wizard-input",
    min: "0",
    value: String(draft.trigger.cooldown_after_run_minutes ?? ""),
    placeholder: "no cooldown",
    style: "width: 90px;",
  });
  cooldownInput.addEventListener("change", () => {
    const n = parseInt(cooldownInput.value, 10);
    if (Number.isFinite(n) && n > 0) draft.trigger.cooldown_after_run_minutes = n;
    else delete draft.trigger.cooldown_after_run_minutes;
  });

  advancedPanel.appendChild(
    el(
      "div",
      { style: "font-size: 11px; font-weight: 500; margin-bottom: 10px;" },
      "Audience caps",
    ),
  );
  advancedPanel.appendChild(
    el(
      "div",
      {
        class: "row",
        style: "gap: 12px; align-items: center; font-size: 11px; margin-bottom: 8px;",
      },
      [
        el("span", { class: "muted", style: "min-width: 130px;" }, "Max contacts/run:"),
        maxPerRunInput,
        el("span", { class: "muted", style: "font-size: 10px;" }, "(0 = unlimited)"),
      ],
    ),
  );
  advancedPanel.appendChild(
    el(
      "div",
      {
        class: "row",
        style: "gap: 12px; align-items: center; font-size: 11px; margin-bottom: 14px;",
      },
      [
        el("span", { class: "muted", style: "min-width: 130px;" }, "Required fields:"),
        requireInput,
        el(
          "span",
          { class: "muted", style: "font-size: 10px;" },
          "(skip contacts missing any of these)",
        ),
      ],
    ),
  );
  advancedPanel.appendChild(
    el(
      "div",
      { style: "font-size: 11px; font-weight: 500; margin-bottom: 10px;" },
      "Trigger gating",
    ),
  );
  advancedPanel.appendChild(
    el(
      "div",
      {
        class: "row",
        style:
          "gap: 8px; align-items: center; font-size: 11px; margin-bottom: 8px; flex-wrap: wrap;",
      },
      [
        el("span", { class: "muted", style: "min-width: 130px;" }, "Working hours:"),
        el("span", { class: "muted" }, "tz"),
        whTzInput,
        el("span", { class: "muted" }, "start"),
        whStartInput,
        el("span", { class: "muted" }, "end"),
        whEndInput,
        el("label", { style: "cursor: pointer;" }, [whWeekdaysOnly, " weekdays only"]),
      ],
    ),
  );
  advancedPanel.appendChild(
    el(
      "div",
      {
        class: "row",
        style: "gap: 12px; align-items: center; font-size: 11px; margin-bottom: 8px;",
      },
      [
        el("span", { class: "muted", style: "min-width: 130px;" }, "Max fires/hour:"),
        maxPerHourInput,
        el("span", { class: "muted", style: "font-size: 10px;" }, "(rate-limits event triggers)"),
      ],
    ),
  );
  advancedPanel.appendChild(
    el("div", { class: "row", style: "gap: 12px; align-items: center; font-size: 11px;" }, [
      el("span", { class: "muted", style: "min-width: 130px;" }, "Cooldown (min):"),
      cooldownInput,
      el(
        "span",
        { class: "muted", style: "font-size: 10px;" },
        "(min minutes between scheduled runs)",
      ),
    ]),
  );

  renderResolvers();
  root.appendChild(resolverList);
  root.appendChild(combineRow);
  root.appendChild(addBar);
  root.appendChild(advancedToggle);
  root.appendChild(advancedPanel);
  return root;
}

function _seedResolverFromCatalog(catalogEntry) {
  // Build a resolver object with the entry's id + default knob values.
  const r = { type: catalogEntry.id };
  for (const knob of catalogEntry.knobs || []) {
    if (knob.default !== undefined) {
      _setNested(r, knob.key, knob.default);
    }
  }
  // Specific seeds for resolvers with required nested shapes.
  if (catalogEntry.id === "crm_query" && !r.filters) r.filters = { tags: [], has: [] };
  if (catalogEntry.id === "manual_list" || catalogEntry.id === "crm_selected") {
    if (!r.contact_ids) r.contact_ids = [];
  }
  return r;
}

function _setNested(obj, dottedKey, value) {
  const parts = String(dottedKey).split(".");
  let cur = obj;
  for (let i = 0; i < parts.length - 1; i++) {
    if (cur[parts[i]] == null || typeof cur[parts[i]] !== "object") cur[parts[i]] = {};
    cur = cur[parts[i]];
  }
  cur[parts[parts.length - 1]] = value;
}

function _getNested(obj, dottedKey) {
  const parts = String(dottedKey).split(".");
  let cur = obj;
  for (const p of parts) {
    if (cur == null) return undefined;
    cur = cur[p];
  }
  return cur;
}

async function _openSourcePickerModal(onPick) {
  const { catalog, families } = await _ensureSourceCatalog();
  if (catalog.length === 0) {
    toast("Source catalog unavailable", "err");
    return;
  }

  const overlay = el("div", {
    style:
      "position: fixed; inset: 0; background: rgba(0,0,0,0.7); z-index: 9999; display: flex; align-items: center; justify-content: center;",
    onclick: (ev) => {
      if (ev.target === ev.currentTarget) overlay.remove();
    },
  });
  const modal = el("div", {
    style:
      "background: var(--bg-1); border: 1px solid var(--border-strong); border-radius: 8px; width: 900px; max-width: 95vw; max-height: 80vh; display: flex; flex-direction: column;",
  });

  const header = el(
    "div",
    {
      class: "row",
      style:
        "padding: 14px 18px; border-bottom: 1px solid var(--border); justify-content: space-between; align-items: center;",
    },
    [
      el("div", { style: "font-weight: 600; font-size: 16px;" }, "Pick an audience source"),
      el(
        "button",
        { class: "btn btn-tiny btn-secondary", type: "button", onclick: () => overlay.remove() },
        "✕",
      ),
    ],
  );

  const body = el("div", {
    style:
      "display: grid; grid-template-columns: 180px 280px 1fr; gap: 0; flex: 1; overflow: hidden;",
  });

  const familyCol = el("div", {
    style: "border-right: 1px solid var(--border); overflow-y: auto;",
  });
  const typeCol = el("div", { style: "border-right: 1px solid var(--border); overflow-y: auto;" });
  const detailCol = el("div", { style: "padding: 16px; overflow-y: auto;" });

  let selectedFamilyId = families[0]?.id || "crm";
  let selectedType = null;

  const renderFamilies = () => {
    familyCol.innerHTML = "";
    families.forEach((f) => {
      const count = catalog.filter((c) => c.family === f.id).length;
      const isOn = f.id === selectedFamilyId;
      familyCol.appendChild(
        el(
          "div",
          {
            style: `padding: 12px 14px; cursor: pointer; border-bottom: 1px solid var(--border); ${isOn ? "background: #1a1a2a; border-left: 2px solid #5a8aff;" : ""}`,
            onclick: () => {
              selectedFamilyId = f.id;
              selectedType = null;
              renderFamilies();
              renderTypes();
              renderDetail();
            },
          },
          [
            el("div", { style: "font-size: 13px;" }, `${f.icon} ${f.label}`),
            el(
              "div",
              { class: "muted", style: "font-size: 10px; margin-top: 2px;" },
              `${count} source${count === 1 ? "" : "s"}`,
            ),
          ],
        ),
      );
    });
  };

  const renderTypes = () => {
    typeCol.innerHTML = "";
    const types = catalog.filter((c) => c.family === selectedFamilyId);
    if (types.length === 0) {
      typeCol.appendChild(
        el(
          "div",
          { class: "muted", style: "padding: 12px; font-size: 12px;" },
          "No sources in this family yet.",
        ),
      );
      return;
    }
    types.forEach((t) => {
      const isOn = selectedType?.id === t.id;
      const isDisabled = t.disabled === true;
      typeCol.appendChild(
        el(
          "div",
          {
            style: `padding: 10px 12px; cursor: ${isDisabled ? "not-allowed" : "pointer"}; border-bottom: 1px solid var(--border); ${isOn ? "background: #1a2a1a; border-left: 2px solid var(--green);" : ""} ${isDisabled ? "opacity: 0.4;" : ""}`,
            onclick: () => {
              if (isDisabled) return;
              selectedType = t;
              renderTypes();
              renderDetail();
            },
          },
          [
            el("div", { style: "font-size: 12px; font-weight: 500;" }, t.label),
            el(
              "div",
              { class: "muted", style: "font-size: 10px; margin-top: 2px; line-height: 1.3;" },
              t.description,
            ),
          ],
        ),
      );
    });
  };

  const renderDetail = () => {
    detailCol.innerHTML = "";
    if (!selectedType) {
      detailCol.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size: 12px;" },
          "Pick a source on the left to see details.",
        ),
      );
      return;
    }
    detailCol.appendChild(
      el("div", { style: "font-weight: 600; font-size: 14px;" }, selectedType.label),
    );
    detailCol.appendChild(
      el(
        "div",
        { class: "muted", style: "font-size: 11px; margin-top: 4px; line-height: 1.4;" },
        selectedType.description,
      ),
    );
    detailCol.appendChild(
      el("div", { style: "margin-top: 12px;" }, [
        el("div", { class: "muted", style: "font-size: 11px;" }, "Compatible platforms:"),
        el(
          "div",
          { class: "row", style: "gap: 6px; margin-top: 4px;" },
          (selectedType.compatible_platforms || []).map((p) =>
            el(
              "span",
              {
                class: "pill",
                style: "background: var(--bg-3); font-size: 10px; padding: 2px 6px;",
              },
              p,
            ),
          ),
        ),
      ]),
    );
    if ((selectedType.knobs || []).length > 0) {
      detailCol.appendChild(
        el(
          "div",
          { class: "muted", style: "font-size: 11px; margin-top: 12px;" },
          `${selectedType.knobs.length} configurable knob${selectedType.knobs.length === 1 ? "" : "s"} — set after adding.`,
        ),
      );
    }
    detailCol.appendChild(
      el(
        "button",
        {
          class: "btn btn-primary",
          type: "button",
          style: "margin-top: 16px;",
          onclick: () => {
            onPick(selectedType);
            overlay.remove();
          },
        },
        "+ Add this source",
      ),
    );
  };

  body.appendChild(familyCol);
  body.appendChild(typeCol);
  body.appendChild(detailCol);
  modal.appendChild(header);
  modal.appendChild(body);
  overlay.appendChild(modal);
  document.body.appendChild(overlay);

  renderFamilies();
  renderTypes();
  renderDetail();
}

function _resolverCard(resolver, idx, draft, onChange) {
  const catalog = _composerSourceCatalog || [];
  const meta = catalog.find((c) => c.id === resolver.type) || null;

  const card = el("div", {
    style:
      "background: var(--bg-0); border: 1px solid var(--border); border-radius: 6px; padding: 12px;",
  });

  const headerRow = el(
    "div",
    {
      class: "row",
      style: "justify-content: space-between; align-items: flex-start; margin-bottom: 8px;",
    },
    [
      el("div", { style: "flex: 1;" }, [
        el("div", { style: "font-weight: 500; font-size: 13px;" }, meta?.label || resolver.type),
        el(
          "div",
          { class: "muted", style: "font-size: 10px; margin-top: 2px;" },
          meta?.description || "",
        ),
      ]),
      el(
        "button",
        {
          class: "btn btn-tiny btn-danger",
          type: "button",
          onclick: () => {
            draft.audience.resolvers.splice(idx, 1);
            onChange();
          },
        },
        "✕",
      ),
    ],
  );
  card.appendChild(headerRow);

  // Per-card preview chip — refreshes via composition-resolve-one
  const previewChip = el(
    "div",
    {
      class: "muted",
      style:
        "font-size: 11px; margin-bottom: 8px; padding: 4px 8px; background: #0a1a0a; border-radius: 3px; display: inline-block;",
    },
    "↻ Compute preview",
  );
  const refreshPreview = async () => {
    previewChip.textContent = "Computing…";
    const r = await api("composition-resolve-one", { body: { resolver } }).catch(() => null);
    if (!r) {
      previewChip.textContent = "Preview failed";
      return;
    }
    previewChip.textContent = `${r.total || 0} contacts match`;
  };
  card.appendChild(previewChip);
  card.appendChild(
    el(
      "button",
      {
        class: "btn btn-tiny btn-secondary",
        type: "button",
        style: "margin-left: 6px; font-size: 10px;",
        onclick: refreshPreview,
      },
      "↻",
    ),
  );

  // Render knobs based on catalog metadata
  const knobsContainer = el("div", {
    style: "margin-top: 8px; display: flex; flex-direction: column; gap: 8px;",
  });
  for (const knob of meta?.knobs || []) {
    knobsContainer.appendChild(
      _renderKnob(knob, resolver, () => {
        refreshPreview();
        onChange();
      }),
    );
  }
  card.appendChild(knobsContainer);

  // Auto-fire preview on first render
  setTimeout(refreshPreview, 100);

  return card;
}

function _renderKnob(knob, resolver, onChange) {
  const wrap = el("div", {});
  if (knob.type === "hidden") return wrap;

  wrap.appendChild(
    el("div", { class: "muted", style: "font-size: 11px; margin-bottom: 2px;" }, knob.label),
  );

  const currentValue = _getNested(resolver, knob.key);

  if (knob.type === "string" || knob.type === "number") {
    let displayValue;
    if (Array.isArray(currentValue)) {
      displayValue = currentValue.join(", ");
    } else {
      displayValue = currentValue ?? "";
    }
    const input = el("input", {
      type: knob.type === "number" ? "number" : "text",
      class: "wizard-input",
      value: displayValue,
      placeholder: knob.placeholder || "",
      style: "width: 100%; max-width: 320px;",
    });
    input.addEventListener("input", () => {
      let v = input.value;
      if (knob.type === "number") v = v === "" ? null : parseFloat(v);
      // Heuristic: if current was an array, preserve array on edit (comma-separated)
      if (Array.isArray(currentValue) || /comma-separated/i.test(knob.label || "")) {
        v = String(input.value)
          .split(",")
          .map((s) => s.trim())
          .filter(Boolean);
        // For contact_ids: numeric coercion
        if (knob.key === "contact_ids") {
          v = v.map((x) => parseInt(x, 10)).filter(Number.isFinite);
        }
      }
      _setNested(resolver, knob.key, v);
    });
    input.addEventListener("blur", onChange);
    wrap.appendChild(input);
  } else if (knob.type === "select") {
    const sel = el(
      "select",
      { class: "wizard-input", style: "max-width: 200px;" },
      (knob.options || []).map((o) => el("option", { value: String(o) }, String(o))),
    );
    sel.value = currentValue ?? knob.default ?? (knob.options?.[0] || "");
    sel.addEventListener("change", () => {
      _setNested(resolver, knob.key, sel.value);
      onChange();
    });
    wrap.appendChild(sel);
  } else if (knob.type === "multi") {
    const row = el("div", { class: "row", style: "gap: 12px; flex-wrap: wrap;" });
    const currentArr = Array.isArray(currentValue) ? currentValue : [];
    (knob.options || []).forEach((opt) => {
      const isOn = currentArr.includes(opt);
      const cb = el("input", { type: "checkbox", checked: isOn ? "checked" : null });
      cb.addEventListener("change", () => {
        let arr = Array.isArray(_getNested(resolver, knob.key))
          ? [..._getNested(resolver, knob.key)]
          : [];
        if (cb.checked) {
          if (!arr.includes(opt)) arr.push(opt);
        } else {
          arr = arr.filter((x) => x !== opt);
        }
        _setNested(resolver, knob.key, arr);
        onChange();
      });
      row.appendChild(el("label", { style: "font-size: 12px; cursor: pointer;" }, [cb, ` ${opt}`]));
    });
    wrap.appendChild(row);
  }
  return wrap;
}
