// dashboard-client-icp-wizard.js — the per-client ICP wizard (Task 9b).
//
// A 4-step modal that runs the "tailor the audience" flow: website+objective
// -> auto-config -> review filters -> preview candidates -> kick scoring with
// a live progress bar. Calls the Task-6 client-ICP HTTP routes
// (client-icp-auto-config / -generate-filters / -candidates / -score / -job)
// via the shared api() dispatch (see dashboard-routes.js / background.js).
//
// Launched from dashboard-crm-browse.js's "Tailor ICP" button, next to the
// per-client filter picker (Task 9a). Classic-script global:
//   window.openClientIcpWizard(clientId, clientName)

const CLIENT_ICP_JOB_POLL_MS = 2000;
const CLIENT_ICP_STEP_LABELS = [
  "Site + objective",
  "Review filters",
  "Preview candidates",
  "Score",
];

function renderClientIcpStepIndicator(step) {
  return el(
    "div",
    { style: "display:flex;gap:6px;margin-bottom:16px;flex-wrap:wrap;" },
    CLIENT_ICP_STEP_LABELS.map((label, i) => {
      const n = i + 1;
      const cls =
        n === step ? "badge badge-purple" : n < step ? "badge badge-green" : "badge badge-gray";
      return el("span", { class: cls, style: "font-size:11px;" }, `${n}. ${label}`);
    }),
  );
}

// ── Step 1 — site + objective ───────────────────────────────────────────
function renderClientIcpStep1(state, ctx) {
  const wrap = el("div", { class: "wizard-section" }, [
    el("div", { class: "wizard-section-title" }, "Site + objective"),
    el(
      "div",
      { class: "muted small", style: "margin-bottom:10px;" },
      "Paste the client's product/marketing site. Claude reads it, infers the services they sell, and drafts a starting filter set from it.",
    ),
  ]);

  const status = el("div", { class: "muted small", style: "margin-top:8px;min-height:16px;" });

  const urlInput = el("input", {
    class: "wizard-input",
    type: "url",
    placeholder: "https://client-site.com",
    value: state.websiteUrl,
    oninput: (e) => {
      state.websiteUrl = e.target.value.trim();
      analyzeBtn.disabled = !state.websiteUrl || state.busy;
    },
  });
  const objectiveInput = el("textarea", {
    class: "wizard-input",
    style: "min-height:70px;resize:vertical;",
    placeholder:
      'Optional: what this client wants out of this campaign (e.g. "find market-making prospects with live tokens")',
    value: state.objective,
    oninput: (e) => {
      state.objective = e.target.value;
    },
  });

  const analyzeBtn = el(
    "button",
    {
      class: "btn btn-primary",
      type: "button",
      disabled: !state.websiteUrl,
      onclick: async () => {
        if (!state.websiteUrl || state.busy) return;
        state.busy = true;
        analyzeBtn.disabled = true;
        analyzeBtn.textContent = "Analyzing…";
        status.textContent = "Fetching the site and inferring services + filters…";
        try {
          const r = await api("client-icp-auto-config", {
            id: state.clientId,
            body: { websiteUrl: state.websiteUrl, objective: state.objective },
          });
          if (r?.error) {
            toast(r.error, "err");
            status.textContent = r.error;
          } else {
            state.services = Array.isArray(r.services) ? r.services : [];
            state.filters = Array.isArray(r.filters) ? r.filters : [];
            state.step = 2;
            state.busy = false;
            ctx.render();
            return;
          }
        } catch (err) {
          toast(err.message, "err");
          status.textContent = err.message;
        }
        state.busy = false;
        analyzeBtn.disabled = !state.websiteUrl;
        analyzeBtn.textContent = "Analyze →";
      },
    },
    "Analyze →",
  );

  wrap.appendChild(
    el("div", {}, [el("label", { class: "wizard-label" }, "Website URL"), urlInput]),
  );
  wrap.appendChild(
    el("div", { style: "margin-top:10px;" }, [
      el("label", { class: "wizard-label" }, "Objective (optional)"),
      objectiveInput,
    ]),
  );
  wrap.appendChild(el("div", { class: "wizard-actions" }, [analyzeBtn]));
  wrap.appendChild(status);
  return wrap;
}

// ── Step 2 — review filters ─────────────────────────────────────────────
function renderClientIcpServiceRow(s) {
  return el("div", { class: "wizard-section", style: "padding:10px 12px;margin-bottom:8px;" }, [
    el("div", { style: "display:flex;align-items:center;gap:8px;margin-bottom:4px;" }, [
      el("strong", { style: "flex:1;" }, s.name || s.id || "Untitled service"),
      el("span", { class: "muted small" }, `w ${Number(s.weight ?? 0).toFixed(2)}`),
    ]),
    el("div", { class: "muted small" }, s.description || ""),
  ]);
}

function renderClientIcpFilterRow(f) {
  const isGate = f.kind === "gate";
  return el("div", { class: "wizard-section", style: "padding:10px 12px;margin-bottom:8px;" }, [
    el("div", { style: "display:flex;align-items:center;gap:8px;margin-bottom:4px;" }, [
      el("strong", { style: "flex:1;" }, f.name || f.id || "Untitled filter"),
      el(
        "span",
        { class: `badge ${isGate ? "badge-red" : "badge-blue"}` },
        isGate ? "gate" : "positive",
      ),
      isGate ? null : el("span", { class: "muted small" }, `w ${Number(f.weight ?? 0).toFixed(2)}`),
    ]),
    el("div", { class: "muted small" }, f.description || ""),
  ]);
}

function renderClientIcpStep2(state, ctx) {
  const wrap = el("div", {}, []);

  const svcBox = el("div", { class: "wizard-section" }, [
    el("div", { class: "wizard-section-title" }, `Services (${state.services.length})`),
  ]);
  if (state.services.length === 0) {
    svcBox.appendChild(
      el("div", { class: "muted small" }, "No services inferred. Go back and try a different URL."),
    );
  } else {
    for (const s of state.services) svcBox.appendChild(renderClientIcpServiceRow(s));
  }
  wrap.appendChild(svcBox);

  const filterBox = el("div", { class: "wizard-section" }, [
    el("div", { class: "wizard-section-title" }, `Filters (${state.filters.length})`),
  ]);
  const regenBtn = el(
    "button",
    {
      class: "btn btn-secondary btn-tiny",
      type: "button",
      onclick: async (ev) => {
        await withProgress(ev.currentTarget, async () => {
          try {
            const r = await api("client-icp-generate-filters", {
              id: state.clientId,
              body: { services: state.services },
            });
            if (r?.error) {
              toast(r.error, "err");
              return;
            }
            state.filters = Array.isArray(r.filters) ? r.filters : [];
            toast(`Regenerated ${state.filters.length} filters`, "ok");
            ctx.render();
          } catch (err) {
            toast(err.message, "err");
          }
        });
      },
    },
    "↻ Regenerate filters",
  );
  filterBox.appendChild(regenBtn);
  if (state.filters.length === 0) {
    filterBox.appendChild(
      el("div", { class: "muted small", style: "margin-top:8px;" }, "No filters yet."),
    );
  } else {
    for (const f of state.filters) filterBox.appendChild(renderClientIcpFilterRow(f));
  }
  wrap.appendChild(filterBox);

  wrap.appendChild(
    el("div", { class: "wizard-actions" }, [
      el(
        "button",
        {
          class: "btn btn-secondary",
          type: "button",
          onclick: () => {
            state.step = 1;
            ctx.render();
          },
        },
        "← Back",
      ),
      el(
        "button",
        {
          class: "btn btn-primary",
          type: "button",
          disabled: state.filters.length === 0,
          onclick: () => {
            state.step = 3;
            ctx.render();
          },
        },
        "Next →",
      ),
    ]),
  );
  return wrap;
}

// ── Step 3 — preview candidates ─────────────────────────────────────────
function renderClientIcpStep3(state, ctx) {
  const wrap = el("div", { class: "wizard-section" }, [
    el("div", { class: "wizard-section-title" }, "Preview candidates"),
    el(
      "div",
      { class: "muted small", style: "margin-bottom:10px;" },
      "Rank a capped set of CRM companies against these filters before committing to a full scoring run.",
    ),
  ]);

  const capInput = el("input", {
    class: "wizard-input",
    type: "number",
    min: "1",
    max: "500",
    value: String(state.cap),
    style: "max-width:120px;",
    oninput: (e) => {
      const n = Math.max(1, Math.min(500, parseInt(e.target.value, 10) || 200));
      state.cap = n;
      // Reflect the clamp back into the field — otherwise typing e.g. 9999
      // shows 9999 while the request that actually goes out sends 500.
      e.target.value = String(n);
    },
  });
  wrap.appendChild(
    el("div", {}, [el("label", { class: "wizard-label" }, "Candidate cap (max 500)"), capInput]),
  );

  const previewStatus = el("div", { class: "muted small", style: "margin-top:8px;" });
  const previewResults = el("div", { style: "margin-top:8px;" });

  const previewBtn = el(
    "button",
    {
      class: "btn btn-secondary",
      type: "button",
      onclick: async (ev) => {
        await withProgress(ev.currentTarget, async () => {
          previewStatus.textContent = "Ranking candidates…";
          previewResults.innerHTML = "";
          try {
            const r = await api("client-icp-candidates", {
              id: state.clientId,
              body: { objective: state.objective, services: state.services, cap: state.cap },
            });
            if (r?.error) {
              toast(r.error, "err");
              previewStatus.textContent = r.error;
              return;
            }
            state.candidatePreview = r;
            previewStatus.textContent = `${r.count} candidate${r.count === 1 ? "" : "s"} matched (cap ${r.cap}).`;
            previewResults.appendChild(
              el(
                "div",
                { style: "display:flex;flex-direction:column;gap:4px;" },
                (r.sample || []).map((c) =>
                  el(
                    "div",
                    { class: "muted small" },
                    `#${c.rank ?? "?"} · ${c.name || `company ${c.id}`}`,
                  ),
                ),
              ),
            );
          } catch (err) {
            toast(err.message, "err");
            previewStatus.textContent = err.message;
          }
        });
      },
    },
    "Preview",
  );

  const scoreBtn = el(
    "button",
    {
      class: "btn btn-primary",
      type: "button",
      onclick: async (ev) => {
        await withProgress(ev.currentTarget, async () => {
          try {
            const r = await api("client-icp-score", {
              id: state.clientId,
              body: {
                objective: state.objective,
                services: state.services,
                filters: state.filters,
                cap: state.cap,
              },
            });
            if (r?.error) {
              toast(r.error, "err");
              return;
            }
            state.jobId = r.jobId;
            ctx.startScoring();
          } catch (err) {
            toast(err.message, "err");
          }
        });
      },
    },
    "Score these →",
  );

  wrap.appendChild(el("div", { class: "wizard-actions" }, [previewBtn, scoreBtn]));
  wrap.appendChild(previewStatus);
  wrap.appendChild(previewResults);
  wrap.appendChild(
    el("div", { class: "wizard-actions", style: "margin-top:16px;" }, [
      el(
        "button",
        {
          class: "btn btn-secondary",
          type: "button",
          onclick: () => {
            state.step = 2;
            ctx.render();
          },
        },
        "← Back",
      ),
    ]),
  );
  return wrap;
}

// ── Step 4 — progress ───────────────────────────────────────────────────
function renderClientIcpStep4(state, ctx) {
  const wrap = el("div", { class: "wizard-section" }, [
    el("div", { class: "wizard-section-title" }, "Scoring"),
  ]);

  const total = state.candidateCount || 0;
  const done = (state.scoredCount || 0) + (state.failedCount || 0);
  const pct = total > 0 ? Math.min(100, Math.round((done / total) * 100)) : 0;
  const isRunning = state.jobStatus === "running";

  wrap.appendChild(
    el("div", { class: "channel-bar", style: "height:8px;margin-bottom:8px;" }, [
      el("div", {
        class: `channel-bar-fill ${state.jobStatus === "error" ? "down" : "healthy"}`,
        style: `width:${pct}%;`,
      }),
    ]),
  );
  wrap.appendChild(
    el("div", { class: "muted small" }, `${done} / ${total || "?"} scored${isRunning ? "…" : ""}`),
  );
  if (state.lastCompany) {
    wrap.appendChild(
      el("div", { class: "muted small", style: "margin-top:4px;" }, `Last: ${state.lastCompany}`),
    );
  }
  if (state.jobError) {
    wrap.appendChild(
      el("div", { class: "badge badge-red", style: "margin-top:8px;" }, state.jobError),
    );
  }

  if (!isRunning) {
    wrap.appendChild(
      el(
        "div",
        { class: "badge badge-green", style: "margin-top:12px;" },
        `Scored ${state.scoredCount || 0} compan${(state.scoredCount || 0) === 1 ? "y" : "ies"}`,
      ),
    );
    wrap.appendChild(
      el("div", { class: "wizard-actions", style: "margin-top:16px;" }, [
        el(
          "button",
          {
            class: "btn btn-primary",
            type: "button",
            onclick: () => {
              // Scope the Contacts tab to this client via the Task-9a hook
              // dashboard-crm-browse.js exposes on window (see
              // window.applyClientIcpResult there for why it's a window
              // property rather than a bare cross-file reference).
              window.applyClientIcpResult(state.clientId);
              ctx.close();
              navigate("crm");
            },
          },
          "View in CRM",
        ),
        el(
          "button",
          { class: "btn btn-secondary", type: "button", onclick: () => ctx.close() },
          "Close",
        ),
      ]),
    );
  }

  return wrap;
}

// ── Orchestrator ─────────────────────────────────────────────────────────
window.openClientIcpWizard = function openClientIcpWizard(clientId, clientName) {
  if (!clientId) {
    toast("Select a client first", "err");
    return;
  }

  const state = {
    step: 1,
    clientId,
    clientName: clientName || `Client #${clientId}`,
    websiteUrl: "",
    objective: "",
    services: [],
    filters: [],
    cap: 200,
    candidatePreview: null,
    jobId: null,
    jobStatus: null,
    candidateCount: 0,
    scoredCount: 0,
    failedCount: 0,
    lastCompany: null,
    jobError: null,
    busy: false,
  };

  // Poll-timer management: `pollHandle` lives only in this closure. It is
  // cleared whenever a terminal job status arrives AND on modal close (via
  // openModal's onClose below) — the two paths a running poll can end on.
  // Without the onClose hook, closing the modal mid-job would leave the
  // interval firing forever against a body that no longer exists.
  let pollHandle = null;
  // Consecutive (not total) failed polls — reset to 0 on any successful
  // poll, so one flaky request doesn't count toward the give-up threshold.
  let consecutivePollErrors = 0;
  const stopPolling = () => {
    if (pollHandle) {
      clearInterval(pollHandle);
      pollHandle = null;
    }
  };

  // Bare div — dashboard.css defines no `.wizard-body` rule, and the
  // reference wizard (dashboard-score-a-company-wizard-ai-filt.js) uses an
  // unclassed wrapper for the equivalent container, so this matches it.
  const wizardBody = el("div", {});

  const modalHandle = openModal({
    title: `Tailor ICP: ${state.clientName}`,
    subtitle:
      "Analyze the client's site, review the filters it generates, preview matching companies, then score them.",
    body: wizardBody,
    maxWidth: "620px",
    ariaLabel: `Tailor ICP for ${state.clientName}`,
    onClose: stopPolling,
  });

  const ctx = {
    render,
    close: modalHandle.close,
    startScoring,
  };

  function render() {
    wizardBody.innerHTML = "";
    wizardBody.appendChild(renderClientIcpStepIndicator(state.step));
    if (state.step === 1) wizardBody.appendChild(renderClientIcpStep1(state, ctx));
    else if (state.step === 2) wizardBody.appendChild(renderClientIcpStep2(state, ctx));
    else if (state.step === 3) wizardBody.appendChild(renderClientIcpStep3(state, ctx));
    else wizardBody.appendChild(renderClientIcpStep4(state, ctx));
  }

  function startScoring() {
    state.step = 4;
    state.jobStatus = "running";
    render();
    pollJob();
    pollHandle = setInterval(pollJob, CLIENT_ICP_JOB_POLL_MS);
  }

  async function pollJob() {
    if (!state.jobId) return;
    let r;
    try {
      r = await api("client-icp-job", { id: state.clientId, jobId: state.jobId });
    } catch (err) {
      // A single transient network hiccup is not worth interrupting the
      // user over — the next tick retries. But 3 in a row means the bot is
      // actually unreachable (down / session expired), and the codebase
      // bans letting that go silent: without this the user would be stuck
      // staring at a frozen progress bar forever with no feedback.
      consecutivePollErrors += 1;
      if (consecutivePollErrors >= 3) {
        stopPolling();
        state.jobStatus = "error";
        state.jobError = `Lost connection while polling (${err?.message || "network error"})`;
        toast("Lost connection to the scoring job. Check the CRM for results", "err");
        render();
      }
      return;
    }
    consecutivePollErrors = 0;
    if (r?.error) {
      stopPolling();
      state.jobStatus = "error";
      state.jobError = r.error;
      toast(r.error, "err");
      render();
      return;
    }
    state.jobStatus = r.status;
    state.candidateCount = r.candidate_count;
    state.scoredCount = r.scored_count;
    state.failedCount = r.failed_count;
    state.lastCompany = r.last_company;
    state.jobError = r.error || null;
    if (r.status !== "running") stopPolling();
    render();
  }

  render();
};
