// ── Control-plane: New-tenant wizard + provisioning progress ──────────────

const WIZ_STEPS = ["Basics", "Contact", "Admin", "Provision"];
const EMAIL_RE = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
const EMPTY_CONTACT = { name: "", role: "", email: "", phone: "" };
try { localStorage.removeItem("cp.newTenantDraft.v1"); } catch {} // old browser-only drafts

// New tenant: always starts blank. Progress lives on the tenant record once
// Basics is saved (a `draft` tenant with setup.step) — `resume` reopens such a
// draft at the step it was left on, from any browser.
function CpCreateWizard({ onClose, onProvisioned, resume }) {
  const notify = useToast();
  const [step, setStep] = React.useState(resume ? Math.min(resume.setupStep ?? 1, 2) : 0);
  const [busy, setBusy] = React.useState(false);
  const [tenant, setTenant] = React.useState(resume || null); // set once Basics is saved
  const [jobId, setJobId] = React.useState(null);              // set when provisioning starts
  const [cfg, setCfg] = React.useState(null);
  const [slugsTaken, setSlugsTaken] = React.useState(null);

  // step 0
  const [b, setB] = React.useState(resume ? { name: resume.name, slug: resume.slug, plan: resume.plan } : { name: "", slug: "", plan: "standard" });
  const auto = (v) => v.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
  // step 1
  const [contacts, setContacts] = React.useState([]);
  const [draft, setDraft] = React.useState(EMPTY_CONTACT);
  // step 2
  const [adminEmail, setAdminEmail] = React.useState(resume ? resume.adminEmail || "" : "");

  React.useEffect(() => {
    cpApi.config().then(setCfg).catch(() => {});
    // Deleted tenants release their slug, so only live ones count.
    cpApi.listTenants().then((r) => setSlugsTaken(new Set((r.data || []).filter((t) => !t.slugReleased).map((t) => t.slug)))).catch(() => {});
    if (resume) {
      cpApi.getContacts(resume.id)
        .then((d) => setContacts((d.data || d).map((c) => ({ name: c.name, role: c.role || "", email: c.email || "", phone: c.phone || "", isPrimary: !!c.is_primary, saved: true }))))
        .catch(() => {});
    }
  }, []);

  // ── autosave to the tenant record (once it exists) ──
  const [serverSave, setServerSave] = React.useState(null); // null | "saving" | "saved" | error text
  const synced = React.useRef(tenant ? { name: tenant.name, plan: tenant.plan, admin_email: tenant.adminEmail || "" } : null);
  const saveProgress = (t, nextStep) => cpApi.updateTenant(t.id, { setup_step: nextStep }).catch(() => {});
  React.useEffect(() => {
    if (!tenant || jobId) return;
    const want = { name: b.name.trim(), plan: b.plan, admin_email: adminEmail.trim() };
    const last = synced.current || {};
    const patch = {};
    for (const k of Object.keys(want)) if (want[k] !== last[k]) patch[k] = want[k];
    if (!Object.keys(patch).length) return;
    if (patch.name !== undefined && patch.name.length < 2) return;
    if (patch.admin_email && !EMAIL_RE.test(patch.admin_email)) return;
    const id = setTimeout(async () => {
      setServerSave("saving");
      try {
        await cpApi.updateTenant(tenant.id, patch);
        synced.current = { ...last, ...patch };
        setServerSave("saved");
      } catch (e) { setServerSave(e.message); }
    }, 800);
    return () => clearTimeout(id);
  }, [tenant, b.name, b.plan, adminEmail, jobId]);

  // ── validation ──
  const baseDomain = cfg ? cfg.baseDomain : "<base domain>";
  const slugIssue = tenant ? null
    : !b.slug ? "Required"
    : b.slug.length < 2 ? "At least 2 characters"
    : cfg && cfg.reservedSlugs.includes(b.slug) ? `"${b.slug}" is reserved`
    : slugsTaken && slugsTaken.has(b.slug) ? `"${b.slug}" is already taken`
    : null;
  const nameIssue = !b.name.trim() ? "Required" : b.name.trim().length < 2 ? "At least 2 characters" : null;
  const draftEmailIssue = draft.email && !EMAIL_RE.test(draft.email) ? "Not a valid email" : null;
  const adminEmailIssue = adminEmail && !EMAIL_RE.test(adminEmail.trim()) ? "Not a valid email" : null;
  const canNext = step === 0 ? !nameIssue && !slugIssue
    : step === 1 ? !draftEmailIssue
    : !adminEmailIssue;

  const ensureTenant = async () => {
    if (tenant) return tenant;
    const { tenant: t } = await cpApi.createTenant({ name: b.name.trim(), slug: b.slug, plan: b.plan });
    synced.current = { name: t.name, plan: t.plan, admin_email: "" };
    setTenant(t);
    return t;
  };

  const addDraftContact = () => {
    if (!draft.name.trim() || draftEmailIssue) return contacts;
    const next = [...contacts, { ...draft, isPrimary: contacts.length === 0 }];
    setContacts(next); setDraft(EMPTY_CONTACT);
    return next;
  };

  const goNext = async () => {
    if (!canNext) return;
    setBusy(true);
    try {
      if (step === 0) {
        const t = await ensureTenant(); // created with setup.step 1
        setStep(1); notify(`"${t.slug}" saved as a draft — resume it any time from the tenants list`);
      } else if (step === 1) {
        const all = addDraftContact(); // a filled-in but un-added contact still counts
        const t = await ensureTenant();
        for (const c of all) if (!c.saved && c.name.trim()) await cpApi.addContact(t.id, c);
        setContacts(all.map((c) => ({ ...c, saved: true })));
        const primary = all.find((c) => c.isPrimary) || all[0];
        if (!adminEmail && primary && primary.email) setAdminEmail(primary.email);
        await saveProgress(t, 2);
        setStep(2);
      } else if (step === 2) {
        const t = await ensureTenant();
        await cpApi.updateTenant(t.id, { admin_email: adminEmail.trim() });
        const res = await cpApi.provisionTenant(t.id);
        setJobId(res.jobId);
        setStep(3);
      }
    } catch (e) { notify(e.message); }
    setBusy(false);
  };

  // step 3 renders the standalone progress view
  if (step === 3 && jobId) {
    return <ProvisioningView jobId={jobId} tenant={tenant}
      onClose={onClose}
      onDone={() => { onProvisioned && onProvisioned(tenant); }} />;
  }

  const status = serverSave === "saving" ? "Saving…"
    : serverSave && serverSave !== "saved" ? `Couldn't save: ${serverSave}`
    : tenant ? "✓ Saved as draft" : null;
  const ok = (msg) => <span style={{ color: "var(--ok)" }}>✓ {msg}</span>;
  const bad = (msg) => <span style={{ color: "var(--danger)" }}>{msg}</span>;
  const real = cfg && cfg.atlasMode === "real";

  return (
    <Modal title={resume ? `Continue setting up ${resume.name}` : "New tenant"} onClose={onClose} wide>
      <div className="tabbar" style={{ marginBottom: 22 }}>
        {WIZ_STEPS.map((s, i) => (
          <button key={s} aria-selected={i === step} disabled
            style={i < step ? { color: "var(--ok)" } : {}}>{i < step ? "✓ " : ""}{s}</button>
        ))}
      </div>

      {step === 0 && (
        <div>
          <SectionHeader title="Basic information" desc="Name the school and pick its subdomain slug." />
          <Field label="School / client name" required max={120} value={b.name}
            hint={b.name && nameIssue ? bad(nameIssue) : null}>
            <input autoFocus value={b.name} maxLength={140} placeholder="e.g. Northwood Public School"
              onChange={(e) => {
                const name = e.target.value;
                // The slug follows the name until it's been typed to something else.
                setB((x) => ({ ...x, name, slug: tenant || (x.slug && x.slug !== auto(x.name)) ? x.slug : auto(name) }));
              }} />
          </Field>
          <div className="row2">
            <Field label="Slug" required
              hint={tenant ? `${tenant.slug}.${baseDomain} — fixed once registered`
                : !b.slug ? `<slug>.${baseDomain}`
                : slugIssue ? bad(slugIssue)
                : ok(`${b.slug}.${baseDomain} is available`)}>
              <input value={b.slug} disabled={!!tenant} placeholder="northwood"
                onChange={(e) => setB((x) => ({ ...x, slug: auto(e.target.value) }))} />
            </Field>
            <Field label="Plan">
              <select value={b.plan} onChange={(e) => setB((x) => ({ ...x, plan: e.target.value }))}>
                <option value="standard">standard</option><option value="pro">pro</option><option value="trial">trial</option>
              </select>
            </Field>
          </div>
        </div>
      )}

      {step === 1 && (
        <div>
          <SectionHeader title="Points of contact" desc="Who runs this school. The first becomes the primary contact." />
          {contacts.map((c, i) => (
            <div key={i} className="person-card" style={{ marginBottom: 10, display: "flex", justifyContent: "space-between", alignItems: "center" }}>
              <div><strong>{c.name}</strong> {c.isPrimary && <Pill tone="accent">Primary</Pill>}
                <div style={{ fontSize: 13, color: "var(--ink-mute)" }}>{[c.role, c.email, c.phone].filter(Boolean).join(" · ")}</div></div>
              {!c.saved && <button className="btn sm ghost" style={{ color: "var(--danger)" }}
                onClick={() => setContacts((cs) => cs.filter((_, j) => j !== i).map((x, j) => ({ ...x, isPrimary: j === 0 })))}>Remove</button>}
            </div>
          ))}
          <div className="panel pad" style={{ background: "var(--panel-2)" }}>
            <Field label="Name"><input value={draft.name} onChange={(e) => setDraft({ ...draft, name: e.target.value })} placeholder="Contact name" /></Field>
            <div className="row2">
              <Field label="Role"><RoleSelect value={draft.role} onChange={(e) => setDraft({ ...draft, role: e.target.value })} /></Field>
              <Field label="Phone"><input type="tel" value={draft.phone} onChange={(e) => setDraft({ ...draft, phone: e.target.value })} placeholder="+91…" /></Field>
            </div>
            <Field label="Email" hint={draftEmailIssue ? bad(draftEmailIssue) : null}>
              <input type="email" value={draft.email} onChange={(e) => setDraft({ ...draft, email: e.target.value })} placeholder="name@school.edu" />
            </Field>
            <button className="btn sm" disabled={!draft.name.trim() || !!draftEmailIssue} onClick={addDraftContact}>
              + Add this contact
            </button>
          </div>
          <p style={{ color: "var(--ink-mute)", fontSize: 13, marginTop: 12 }}>
            {contacts.length === 0 ? "Optional — you can add contacts later on the tenant page." : `${contacts.length} contact${contacts.length === 1 ? "" : "s"} will be saved when you continue.`}
          </p>
        </div>
      )}

      {step === 2 && (
        <div>
          <SectionHeader title="School administrator"
            desc="The superadmin account created when the ecosystem is provisioned. A one-time password is generated." />
          <Field label="Admin email" hint={adminEmailIssue ? bad(adminEmailIssue) : `Defaults to the primary contact's email. Empty = admin@${b.slug}.local`}>
            <input type="email" value={adminEmail} onChange={(e) => setAdminEmail(e.target.value)} placeholder={`admin@${b.slug || "school"}.edu`} />
          </Field>
          <div className="panel pad" style={{ background: "var(--panel-2)", fontSize: 13, color: "var(--ink-soft)", lineHeight: 1.6 }}>
            <div style={{ fontWeight: 700, color: "var(--ink)", marginBottom: 6 }}>Ready to create</div>
            <div className="mono">{tenant ? tenant.slug : b.slug}.{baseDomain} · {b.plan} · {contacts.length} contact{contacts.length === 1 ? "" : "s"}</div>
            <div style={{ marginTop: 8 }}>
              {real
                ? <>Creates Atlas project <code>lms-{b.slug}</code> with a free M0 cluster in {cfg.atlasRegion} (usually 3–7 minutes), a database user, then the schema and the admin account.</>
                : <>Mock mode — creates database <code>lms_{b.slug.replace(/-/g, "_")}</code> on the local MongoDB, then the schema and the admin account. Takes a few seconds.</>}
            </div>
            <div style={{ marginTop: 6 }}>You can close the progress window — it keeps running, and the tenant page shows every step.</div>
          </div>
        </div>
      )}

      <WizardFooter
        step={step} busy={busy} canNext={canNext} status={status}
        nextLabel={step === 2 ? "Create ecosystem →" : "Next →"}
        onCancel={onClose}
        onBack={() => { const s = step - 1; setStep(s); if (tenant) saveProgress(tenant, s); }}
        onNext={goNext}
      />
    </Modal>
  );
}

// ── Provisioning steps checklist (wizard + tenant page) ───────────────────
const DEFAULT_PROVISION_STEPS = [
  { key: "project", label: "Creating the Atlas project" },
  { key: "project_access", label: "Checking Atlas permissions" },
  { key: "cluster", label: "Requesting the free cluster" },
  { key: "cluster_wait", label: "Creating free cluster (a few minutes)" },
  { key: "db_user", label: "Creating the database user" },
  { key: "access_list", label: "Allowing the LMS servers" },
  { key: "connection", label: "Connecting to the database" },
  { key: "schema", label: "Building the schema" },
  { key: "seed", label: "Seeding roles & the admin account" },
  { key: "finalise", label: "Finalising" },
];
const fmtDur = (sec) => `${Math.floor(sec / 60)}:${String(Math.max(0, sec) % 60).padStart(2, "0")}`;

function JobSteps({ job }) {
  const running = !!job && (job.status === "running" || job.status === "queued");
  const [, tick] = React.useState(0);
  React.useEffect(() => {
    if (!running) return;
    const id = setInterval(() => tick((n) => n + 1), 1000);
    return () => clearInterval(id);
  }, [running]);

  const steps = (job && job.result && job.result.steps) || DEFAULT_PROVISION_STEPS;
  const current = job && job.result && job.result.step;
  let idx = job && job.status === "done" ? steps.length : steps.findIndex((s) => s.key === current);
  if (idx < 0 && job && job.status === "running") idx = 0;
  const started = job && job.started_at ? new Date(job.started_at).getTime() : null;
  const ended = job && job.finished_at ? new Date(job.finished_at).getTime() : null;
  const elapsed = started ? Math.floor(((ended || Date.now()) - started) / 1000) : null;
  const doneCount = Math.max(0, Math.min(idx, steps.length));

  return (
    <div>
      <div style={{ display: "flex", gap: 12, alignItems: "center", fontSize: 13, color: "var(--ink-mute)", marginBottom: 12 }}>
        <span>{doneCount} of {steps.length} steps done</span>
        {elapsed != null && <span className="mono">· {running ? "running" : "took"} {fmtDur(elapsed)}</span>}
        {job && job.status === "queued" && <span>· waiting for a worker…</span>}
      </div>
      <div style={{ height: 4, background: "var(--panel-2)", borderRadius: 2, marginBottom: 14, overflow: "hidden" }}>
        <div style={{ height: "100%", width: `${(doneCount / steps.length) * 100}%`, transition: "width .4s",
          background: job && job.status === "error" ? "var(--danger)" : "var(--ok)" }} />
      </div>
      <div style={{ display: "grid", gap: 10, marginBottom: 14 }}>
        {steps.map((s, i) => {
          const state = job && job.status === "error" && i === idx ? "error"
            : i < idx ? "done" : i === idx && running ? "active" : "pending";
          return (
            <div key={s.key} style={{ display: "flex", alignItems: "center", gap: 12 }}>
              <span style={{
                width: 22, height: 22, borderRadius: "50%", display: "flex", alignItems: "center", justifyContent: "center",
                fontSize: 12, fontWeight: 700, flex: "none",
                background: state === "done" ? "var(--ok)" : state === "error" ? "var(--danger)" : state === "active" ? "var(--accent-soft)" : "var(--panel-2)",
                color: state === "done" || state === "error" ? "#fff" : state === "active" ? "var(--accent)" : "var(--ink-mute)",
                border: state === "pending" ? "1px solid var(--line-strong)" : "none",
              }}>
                {state === "done" ? "✓" : state === "error" ? "✕" : state === "active" ? <Spinner /> : i + 1}
              </span>
              <span style={{ fontWeight: state === "active" ? 700 : 500, color: state === "pending" ? "var(--ink-mute)" : "var(--ink)" }}>{s.label}</span>
              <span style={{ fontSize: 12, color: state === "error" ? "var(--danger)" : "var(--ink-mute)" }}>
                {state === "done" ? "done" : state === "active" ? "in progress" : state === "error" ? "failed" : "pending"}
              </span>
            </div>
          );
        })}
      </div>
      {job && job.status === "error" && (
        <div style={{ color: "var(--danger)", marginBottom: 12, fontSize: 14 }}>{job.kind === "delete" ? "Deletion" : "Provisioning"} failed: {job.error}</div>
      )}
      {job && job.log && (
        <details open={job.status === "error"}>
          <summary style={{ cursor: "pointer", fontSize: 13, color: "var(--ink-soft)", marginBottom: 8 }}>Log</summary>
          <pre style={{ background: "var(--panel-2)", border: "1px solid var(--line)", borderRadius: 8, padding: 12,
            fontSize: 12, fontFamily: "var(--mono)", maxHeight: 220, overflow: "auto", margin: 0, whiteSpace: "pre-wrap" }}>
            {job.log.trim()}
          </pre>
        </details>
      )}
    </div>
  );
}

// ── Provisioning progress (modal) ─────────────────────────────────────────
// `stay`: closing just closes (e.g. a delete started from the tenants list)
// instead of opening the tenant page.
function ProvisioningView({ jobId, tenant, title, onClose, onDone, stay }) {
  const [job, setJob] = React.useState(null);

  React.useEffect(() => {
    let stop = false;
    const tick = async () => {
      try {
        const { job } = await cpApi.getJob(jobId);
        if (stop) return;
        setJob(job);
        if (job.status === "done" || job.status === "error") { onDone && onDone(); return; }
      } catch {}
      if (!stop) setTimeout(tick, 1500);
    };
    tick();
    return () => { stop = true; };
  }, [jobId]);

  const finished = job && (job.status === "done" || job.status === "error");
  const openTenant = () => { onClose(); if (tenant && !stay) window.location.hash = `#/tenants/${tenant.id}`; };

  return (
    <Modal title={title || `Creating ${tenant ? tenant.name : "the ecosystem"}`} onClose={finished ? onClose : openTenant} wide>
      <JobSteps job={job} />

      {job && job.status === "done" && job.result && job.kind !== "delete" && (
        <div style={{ marginTop: 16 }}>
          <SectionHeader title="Ecosystem ready" desc="Hand these credentials to the school administrator." />
          <div style={{ fontSize: 13, marginBottom: 8 }}>Admin email: <code>{job.result.superadminEmail}</code></div>
          {job.result.oneTimePassword
            ? <CopyBox value={job.result.oneTimePassword} />
            : (
              <div style={{ fontSize: 13, color: "var(--ink-soft)" }}>
                The one-time password was already shown and can't be displayed again.
                {tenant && <> Generate a new one under <a href={`#/tenants/${tenant.id}/admin`} onClick={onClose}>Admin login → Reset admin password</a>.</>}
              </div>
            )}
          <p style={{ fontSize: 12, color: "var(--ink-mute)", marginTop: 8 }}>
            Shown once here. They'll be asked to set a new password on first sign-in. Lost it? Use
            Admin login → Reset admin password on the tenant.
          </p>
        </div>
      )}

      <div className="wizard-foot">
        <span style={{ fontSize: 12, color: "var(--ink-mute)" }}>
          {finished ? "Powered by EpiqMinds" : "Safe to close — it keeps running and the tenant page shows progress."}
        </span>
        <div className="spacer" />
        {finished
          ? <button className="btn primary" onClick={openTenant}>{stay ? "Close" : "Open tenant"}</button>
          : <button className="btn" onClick={openTenant}>Run in background</button>}
      </div>
    </Modal>
  );
}

window.CpCreateWizard = CpCreateWizard;
window.ProvisioningView = ProvisioningView;
window.JobSteps = JobSteps;
