const { PillButton, Icon } = window.AequitasDS;
const C = window.CAREERS;

const STORE = "aeq_application_draft";
const words = (s) => (s || "").trim() ? (s || "").trim().split(/\s+/).length : 0;

/* `name` puts the field key in the DOM so a failed step can scroll to the first
   problem. The error replaces the help text rather than stacking under it —
   nothing moves, and the correction is where the guidance was. */
function Field({ label, optional, help, children, full, error, name }) {
  return (
    <div className={`ap-f${full ? " ap-f--full" : ""}${error ? " ap-f--err" : ""}`} data-field={name}>
      <label>{label}{optional && <em>optional</em>}</label>
      {children}
      {error
        ? <span className="ap-f__err" role="alert">{error}</span>
        : help ? <span className="ap-f__help">{help}</span> : null}
    </div>
  );
}

function Text({ v, on, ph, type }) {
  return <input type={type || "text"} value={v || ""} placeholder={ph} onChange={(e) => on(e.target.value)} />;
}

function Choose({ v, on, options, ph }) {
  return (
    <select value={v || ""} onChange={(e) => on(e.target.value)}>
      <option value="">{ph || "Select"}</option>
      {options.map((o) => <option key={o} value={o}>{o}</option>)}
    </select>
  );
}

function Chips({ v, on, options, multi }) {
  const set = Array.isArray(v) ? v : [];
  const toggle = (o) => {
    if (!multi) return on(o);
    on(set.includes(o) ? set.filter((x) => x !== o) : set.concat(o));
  };
  return (
    <div className="ap-choices">
      {options.map((o) => (
        <button key={o} type="button" className={`ap-chip${(multi ? set.includes(o) : v === o) ? " is-on" : ""}`} onClick={() => toggle(o)}>{o}</button>
      ))}
    </div>
  );
}

function Binary({ v, on }) {
  return (
    <div className="ap-binary">
      <button type="button" className={v === "Yes" ? "is-on" : ""} onClick={() => on("Yes")}>Yes</button>
      <button type="button" className={v === "No" ? "is-on" : ""} onClick={() => on("No")}>No</button>
    </div>
  );
}

function Essay({ spec, v, on, error }) {
  const n = words(v);
  return (
    <div className={`ap-f ap-f--full${error ? " ap-f--err" : ""}`} data-field={spec.id}>
      <label>{spec.q}</label>
      <span className="ap-f__help" style={{ marginBottom: 4 }}>{spec.help}</span>
      <textarea value={v || ""} onChange={(e) => on(e.target.value)} placeholder="Write as much or as little as the question needs." />
      {/* The word count stays advisory — over the limit is amber, never blocking. */}
      <span className={`ap-f__count${n > spec.limit ? " is-over" : ""}`}>{n} / {spec.limit} words</span>
      {error ? <span className="ap-f__err" role="alert">{error}</span> : null}
    </div>
  );
}

/* Holds the File itself, not just its name — the prototype kept only the name,
   which meant nothing was ever actually uploaded. Files live outside the autosaved
   draft because a File cannot be serialised to localStorage. */
function Upload({ label, note, optional, v, on, error, name }) {
  const ref = React.useRef(null);
  const kb = v ? Math.round(v.size / 1024) : 0;
  return (
    <div className={`ap-f ap-f--full${error ? " ap-f--err" : ""}`} data-field={name}>
      <label>{label}{optional && <em>optional</em>}</label>
      <div className={`ap-drop${v ? " is-set" : ""}${error ? " ap-drop--err" : ""}`} onClick={() => ref.current && ref.current.click()}>
        <Icon name={v ? "check-circle" : "upload"} size={20} />
        <div>
          <div className="ap-drop__t">{v ? v.name : "Choose a file, or drag it here"}</div>
          <div className="ap-drop__n">{v ? `${kb.toLocaleString()}KB · click to replace` : note}</div>
        </div>
      </div>
      <input
        ref={ref} type="file" style={{ display: "none" }}
        accept=".pdf,.doc,.docx,.rtf,.txt,.csv,.png,.jpg,.jpeg,.zip"
        onChange={(e) => on(e.target.files && e.target.files[0] ? e.target.files[0] : null)}
      />
      {error ? <span className="ap-f__err" role="alert">{error}</span> : null}
    </div>
  );
}

/* Documents go straight from the browser to Blob storage, never through our API —
   a serverless body caps at 4.5MB, which would have limited a CV to about 3MB.
   Must match the cap in api/applications/upload.js. */
const MAX_UPLOAD_BYTES = 25 * 1024 * 1024;

const ACCEPT = {
  pdf: "application/pdf",
  doc: "application/msword",
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  rtf: "application/rtf",
  txt: "text/plain",
  csv: "text/csv",
  png: "image/png",
  jpg: "image/jpeg",
  jpeg: "image/jpeg",
  zip: "application/zip"
};

const contentTypeOf = (file) => ACCEPT[(file.name.split(".").pop() || "").toLowerCase()] || "";

/* ── What each step requires before it will advance ──────────────────────────
   Three things are deliberately NOT required, because the brief was explicit:
     · drawdown and annualised return — asking for them would make the initial
       application depend on performance documentation, which it must not;
     · the word limits — advisory, so an over-long answer still submits;
     · programming experience — "None" is a valid selection and must count.
   Everything else that the firm actually reads is required, so nobody can click
   through six steps and submit an empty application. */
const EMAIL = /^[^@\s]+@[^@\s.]+\.[^@\s]+$/;
const filled = (v) => (Array.isArray(v) ? v.length > 0 : !!String(v == null ? "" : v).trim());

const STEP_RULES = [
  [
    ["name", "Please give your full name."],
    ["email", "Please give an email address.", (v) => (EMAIL.test(String(v).trim()) ? null : "That email address does not look right.")],
    ["phone", "Please give a telephone number."],
    ["country", "Please give your country of residence."]
  ],
  [
    ["years", "Please choose how long you have been trading."],
    ["capacity", "Please say whether you have traded professionally, independently, or both."],
    ["markets", "Please select at least one market."],
    ["instruments", "Please select at least one instrument."],
    ["holding", "Please choose a typical holding period."],
    ["frequency", "Please choose an average trading frequency."],
    ["capital", "Please choose an approximate range."]
  ],
  [
    ["hasRecord", "Please answer yes or no."],
    ["verification", "Please say how the record can be verified.", null, (d) => d.hasRecord === "Yes"],
    ["recordYears", "Please choose how many years it covers.", null, (d) => d.hasRecord === "Yes"]
  ],
  [
    ["e1", "Please answer this question."],
    ["e2", "Please answer this question."],
    ["e3", "Please answer this question."],
    ["e4", "Please answer this question."]
  ],
  [
    ["tech", "Please select what you use, or None."]
  ],
  [
    ["cv", "A CV is required to apply."]
  ]
];

function validateStep(step, d, files) {
  const errs = {};
  for (const [key, required, extra, when] of STEP_RULES[step] || []) {
    if (when && !when(d)) continue;
    const v = step === 5 ? files[key] : d[key];
    if (!(step === 5 ? !!v : filled(v))) { errs[key] = required; continue; }
    if (extra) { const m = extra(v); if (m) errs[key] = m; }
  }
  return errs;
}

function Side({ step, saved }) {
  const notes = [
    ["In confidence", "Your application is seen only by the people assessing it. Nothing is shared outside the firm."],
    ["Verification", "We may ask for evidence of the figures you give here later in the process, under confidentiality."],
    ["No strategies", "We never ask you to disclose proprietary methods. If a question feels like it does, leave it and tell us why."],
    ["Take your time", "Answers are saved as you write. You can close this and return to it."],
    ["Not a filter", "Programming experience is not required, and its absence does not weaken an application."],
    ["Documents", "A CV is enough to apply. Everything else is optional and can follow."]
  ];
  const [t, p] = notes[step] || notes[0];
  return (
    <aside className="ap-side">
      <div className="ap-side__b">
        <div className="ap-side__t">Estimated time</div>
        <div className="ap-side__p">10–15 minutes</div>
      </div>
      <div className="ap-side__b">
        <div className="ap-side__t">{t}</div>
        <div className="ap-side__p">{p}</div>
      </div>
      <div className="ap-side__save"><span className="ap-side__dot"></span>{saved ? "Saved just now" : "Saving as you type"}</div>
    </aside>
  );
}

function ApplicationPage({ go }) {
  const [step, setStep] = React.useState(0);
  const [saved, setSaved] = React.useState(false);
  const [d, setD] = React.useState(() => {
    try { return JSON.parse(localStorage.getItem(STORE) || "{}"); } catch (e) { return {}; }
  });
  const set = (k) => (v) => { clearErr(k); setD((o) => Object.assign({}, o, { [k]: v })); };

  React.useEffect(() => {
    const id = setTimeout(() => {
      try { localStorage.setItem(STORE, JSON.stringify(d)); setSaved(true); setTimeout(() => setSaved(false), 1600); } catch (e) {}
    }, 500);
    return () => clearTimeout(id);
  }, [d]);

  /* Files sit outside `d` because a File cannot be JSON-serialised into the draft.
     A reload therefore keeps every answer and asks only for the documents again. */
  const [files, setFiles] = React.useState({});
  const [busy, setBusy] = React.useState(false);
  const [progress, setProgress] = React.useState("");
  const [error, setError] = React.useState("");
  const [errors, setErrors] = React.useState({});
  // An answered field stops complaining immediately; no need to press Continue again.
  const clearErr = (k) => setErrors((e) => { if (!e[k]) return e; const n = Object.assign({}, e); delete n[k]; return n; });

  const last = C.steps.length - 1;

  const submit = async () => {
    setError("");
    const chosen = Object.keys(files).filter((k) => files[k]);
    const tooBig = chosen.find((k) => files[k].size > MAX_UPLOAD_BYTES);
    if (tooBig) {
      setError(`${files[tooBig].name} is ${(files[tooBig].size / 1048576).toFixed(1)}MB. The limit is 25MB per document.`);
      return;
    }
    const wrongType = chosen.find((k) => !contentTypeOf(files[k]));
    if (wrongType) {
      setError(`${files[wrongType].name} is not a file type we can accept. PDF, Word, RTF, text, images or a zip.`);
      return;
    }

    setBusy(true);
    try {
      /* Uploaded one at a time so `Uploading 2 of 3` is honest, and so a failure
         names the document that failed rather than the whole set. */
      const uploaded = [];
      const fileErrors = [];
      for (let i = 0; i < chosen.length; i++) {
        const field = chosen[i], file = files[field];
        setProgress(`Uploading ${file.name} (${i + 1} of ${chosen.length})…`);
        try {
          const blob = await window.VercelBlob.upload(`applications/${field}-${file.name}`, file, {
            access: "public",
            handleUploadUrl: "/api/applications/upload",
            contentType: contentTypeOf(file)
          });
          uploaded.push({ field, name: file.name, url: blob.url, size: file.size, contentType: contentTypeOf(file) });
        } catch (e) {
          // Never fatal. The application is worth more than any one attachment.
          fileErrors.push(`${file.name} (${field}) failed to upload`);
        }
      }

      setProgress("Submitting…");
      const r = await fetch("/api/applications/submit", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ application: d, files: uploaded, fileErrors })
      });
      const j = await r.json().catch(() => ({}));
      if (!r.ok || !j.success) throw new Error(j.error || "Your application could not be submitted.");
      // Only clear the draft once it is safely stored.
      try { localStorage.removeItem(STORE); } catch (e) {}
      go("submitted", j.reference);
    } catch (e) {
      setError((e && e.message) || "Something went wrong. Please try again.");
    } finally {
      setBusy(false);
      setProgress("");
    }
  };

  /* Put the applicant in front of the first thing that needs fixing, rather than
     leaving them to hunt for a red underline somewhere down the page. */
  const showFirstError = (errs) => {
    const first = Object.keys(errs)[0];
    if (!first) return;
    window.requestAnimationFrame(() => {
      const el = document.querySelector(`[data-field="${first}"]`);
      if (!el) return;
      el.scrollIntoView({ behavior: "smooth", block: "center" });
      const input = el.querySelector("input, textarea, select, button");
      if (input) input.focus({ preventScroll: true });
    });
  };

  const advance = () => {
    if (busy) return;

    const errs = validateStep(step, d, files);
    if (Object.keys(errs).length) {
      setErrors(errs);
      setError("A few answers are still needed on this step.");
      showFirstError(errs);
      return;
    }
    setErrors({});
    setError("");

    if (step < last) { setStep(step + 1); window.scrollTo({ top: 0 }); return; }

    /* Submitting re-checks every step, not just this one. The progress strip lets
       an applicant go back, and going back to empty a field they had already
       answered would otherwise walk straight past its rule. */
    for (let i = 0; i <= last; i++) {
      const e = validateStep(i, d, files);
      if (Object.keys(e).length) {
        setStep(i);
        setErrors(e);
        setError(`Step ${i + 1}, ${C.steps[i].label}, is not complete.`);
        showFirstError(e);
        return;
      }
    }
    submit();
  };
  const back = () => { setStep(Math.max(0, step - 1)); window.scrollTo({ top: 0 }); };

  const heads = [
    ["Personal Information", "Who you are, and how to reach you."],
    ["Trading Background", "Where you have traded, what you have traded, and at what scale."],
    ["Track Record", "What can be evidenced, and how."],
    ["About You", "Four questions. These matter more than everything above them."],
    ["Technical Experience", "What you can use today. This is not a filter."],
    ["Documents", "A CV is enough to apply."]
  ];

  return (
    <div className="ap-shell">
      <header className="ap-bar">
        <div className="ap-bar__in">
          <a href="/" className="aeq-logo aeq-logo--light ap-bar__logo" aria-label="Aequitas Capital Partners — home">
            <span className="aeq-logo__mark"></span>
            <span className="aeq-logo__desc">Capital Partners</span>
          </a>
          <div className="ap-bar__meta">
            <span className="ap-bar__i"><span className="ap-bar__t">Application</span><span className="ap-bar__v">Systematic Quantitative Trader</span></span>
            <span><span className="ap-bar__t">Step</span><span className="ap-bar__v">{step + 1} of {C.steps.length}</span></span>
            <button className="ap-bar__exit" onClick={() => go("role")}>Save and exit</button>
          </div>
        </div>
      </header>

      <nav className="ap-prog">
        <div className="ap-prog__in">
          {C.steps.map((s, i) => (
            <button key={s.id} className={`ap-prog__s${i === step ? " is-on" : ""}${i < step ? " is-done" : ""}`} onClick={() => i <= step && setStep(i)} disabled={i > step}>
              <span className="ap-prog__tick"></span>
              <span className="ap-prog__n">{String(i + 1).padStart(2, "0")}</span>
              <span className="ap-prog__l">{s.label}</span>
            </button>
          ))}
        </div>
      </nav>

      <main className="ap-main">
        <div className="ap-step" key={step}>
          <div>
            <span className="ap-stepnum">Step {step + 1} of {C.steps.length}</span>
            <h1 className="ap-h2">{heads[step][0]}</h1>
            <p className="ap-lede">{heads[step][1]}</p>

            {step === 0 && (
              <div className="ap-fields">
                <Field label="Full name" name="name" error={errors.name}><Text v={d.name} on={set("name")} ph="As it appears on your passport" /></Field>
                <Field label="Email" name="email" error={errors.email}><Text type="email" v={d.email} on={set("email")} ph="you@example.com" /></Field>
                <Field label="Telephone" name="phone" error={errors.phone}><Text type="tel" v={d.phone} on={set("phone")} ph="+44 7700 900000" /></Field>
                <Field label="Country of residence" name="country" error={errors.country}><Text v={d.country} on={set("country")} ph="United Kingdom" /></Field>
                <Field label="LinkedIn" optional full><Text v={d.linkedin} on={set("linkedin")} ph="linkedin.com/in/…" /></Field>
              </div>
            )}

            {step === 1 && (
              <div className="ap-fields">
                <Field label="Current employer" optional help="Leave blank if you trade independently."><Text v={d.employer} on={set("employer")} /></Field>
                <Field label="Previous employer" optional><Text v={d.prevEmployer} on={set("prevEmployer")} /></Field>
                <Field label="Years trading" name="years" error={errors.years}><Choose v={d.years} on={set("years")} options={["1–3", "3–5", "5–10", "10–15", "15+"]} /></Field>
                <Field label="Professional or independent" name="capacity" error={errors.capacity}><Choose v={d.capacity} on={set("capacity")} options={["Professional", "Independent", "Both"]} /></Field>
                <Field label="Primary markets" full name="markets" error={errors.markets} help="Select all that apply."><Chips multi v={d.markets} on={set("markets")} options={["Equities", "Futures", "Options", "Fixed income", "Foreign exchange", "Commodities", "Digital assets"]} /></Field>
                <Field label="Primary instruments" full name="instruments" error={errors.instruments}><Chips multi v={d.instruments} on={set("instruments")} options={["Cash equities", "ETFs", "Index futures", "Single-stock options", "Index options", "Swaps", "Spot FX", "FX forwards", "Perpetuals"]} /></Field>
                <Field label="Typical holding period" name="holding" error={errors.holding}><Choose v={d.holding} on={set("holding")} options={["Intraday", "Days", "Weeks", "Months", "Mixed"]} /></Field>
                <Field label="Average trading frequency" name="frequency" error={errors.frequency}><Choose v={d.frequency} on={set("frequency")} options={["Many times daily", "Daily", "Several times weekly", "Weekly", "Monthly"]} /></Field>
                <Field label="Approximate capital managed" full name="capital" error={errors.capital} help="A range is sufficient. This is context, not a threshold."><Choose v={d.capital} on={set("capital")} options={["Under $250k", "$250k – $1m", "$1m – $5m", "$5m – $25m", "$25m – $100m", "Over $100m"]} /></Field>
              </div>
            )}

            {step === 2 && (
              <React.Fragment>
                <div className="ap-fields">
                  <Field label="Do you have a verifiable trading record?" full name="hasRecord" error={errors.hasRecord}><Binary v={d.hasRecord} on={set("hasRecord")} /></Field>
                  {d.hasRecord === "Yes" && (
                    <React.Fragment>
                      <Field label="Verification method" full name="verification" error={errors.verification}><Chips v={d.verification} on={set("verification")} options={["Broker", "Employer", "Audited", "Prop firm", "Other"]} /></Field>
                      <Field label="Years covered" name="recordYears" error={errors.recordYears}><Choose v={d.recordYears} on={set("recordYears")} options={["Under 1", "1–2", "2–3", "3–5", "5–10", "10+"]} /></Field>
                      <Field label="Maximum drawdown"><Text v={d.drawdown} on={set("drawdown")} ph="e.g. 14%" /></Field>
                      <Field label="Approximate annualised return" full><Text v={d.annualised} on={set("annualised")} ph="e.g. 18%" /></Field>
                    </React.Fragment>
                  )}
                </div>
                <div className="ap-note">
                  <Icon name="info" size={17} />
                  <p><strong>We do not require detailed performance documentation during the initial application.</strong> Figures given here are context for an initial conversation. Verification, where relevant, happens later in the process under appropriate confidentiality arrangements.</p>
                </div>
              </React.Fragment>
            )}

            {step === 3 && (
              <React.Fragment>
                <div className="ap-fields">
                  {C.essays.map((e) => <Essay key={e.id} spec={e} v={d[e.id]} on={set(e.id)} error={errors[e.id]} />)}
                </div>
                <div className="ap-note">
                  <Icon name="shield" size={17} />
                  <p><strong>Do not describe proprietary methods here.</strong> We are not asking how your strategies work, and answers that disclose them will not strengthen an application. Write about judgement, process and what changed your thinking.</p>
                </div>
              </React.Fragment>
            )}

            {step === 4 && (
              <React.Fragment>
                <div className="ap-fields">
                  <Field label="Which of these do you use?" full name="tech" error={errors.tech} help="Select all that apply, or None."><Chips multi v={d.tech} on={set("tech")} options={["Python", "SQL", "Git", "Statistics", "Linux", "None"]} /></Field>
                  <Field label="Anything else worth knowing" optional full help="Other languages, libraries or infrastructure you work with."><Text v={d.techNote} on={set("techNote")} /></Field>
                </div>
                <div className="ap-note">
                  <Icon name="info" size={17} />
                  <p><strong>Programming experience is not required.</strong> Several strong traders here arrived without it. It is useful to know where you are starting from, nothing more.</p>
                </div>
              </React.Fragment>
            )}

            {step === 5 && (
              <React.Fragment>
                <div className="ap-fields">
                  <Upload label="CV" name="cv" error={errors.cv} note="PDF or Word, up to 25MB" v={files.cv} on={(f) => { clearErr("cv"); setFiles((o) => Object.assign({}, o, { cv: f })); }} />
                  <Upload label="Performance summary" optional note="PDF, Word, images or a zip, up to 25MB. Redact anything you are not comfortable sharing at this stage." v={files.perf} on={(f) => setFiles((o) => Object.assign({}, o, { perf: f }))} />
                  <Upload label="Supporting documents" optional note="References, publications, research write-ups. Up to 25MB — zip them if there are several." v={files.support} on={(f) => setFiles((o) => Object.assign({}, o, { support: f }))} />
                </div>
                <div className="ap-note">
                  <Icon name="info" size={17} />
                  <p><strong>How we handle what you send.</strong> Your application is stored securely and seen only by the people at Aequitas assessing it. It is never shared outside the firm and is not used for anything other than this recruitment. Write to us at any time to ask what we hold or to have it deleted.</p>
                </div>
              </React.Fragment>
            )}

            {error ? (
              <div className="ap-note ap-note--error" role="alert">
                <Icon name="alert" size={17} />
                <p><strong>{error}</strong> Nothing you have written has been lost — your answers are still saved on this device.</p>
              </div>
            ) : null}

            <div className="ap-actions">
              {step > 0 && <button className="ap-back" onClick={back} disabled={busy}>Back</button>}
              <PillButton tone="dark" as="button" onClick={advance} disabled={busy}>
                {step === last ? (busy ? (progress || "Submitting…") : "Submit application") : "Continue"}
              </PillButton>
              <span className="ap-actions__r">{step === last ? "You can still return to earlier steps before submitting." : C.steps[step + 1].label + " next"}</span>
            </div>
          </div>

          <Side step={step} saved={saved} />
        </div>
      </main>
    </div>
  );
}

/* ── Application Submitted ── */
function SubmittedPage({ go, reference }) {
  return (
    <div className="ap-done">
      <header className="ap-bar" style={{ background: "transparent" }}>
        <div className="ap-bar__in">
          <a href="/" className="aeq-logo aeq-logo--light ap-bar__logo" aria-label="Aequitas Capital Partners — home">
            <span className="aeq-logo__mark"></span>
            <span className="aeq-logo__desc">Capital Partners</span>
          </a>
        </div>
      </header>
      <div className="ap-done__in">
        <div className="ap-done__rule"></div>
        <div className="ap-done__body">
          <span className="cr-eyebrow cr-eyebrow--light">Systematic Quantitative Trader · London</span>
          <h1 className="ap-done__h" style={{ marginTop: 22 }}>Application<br />Received</h1>
          <p className="ap-done__p">Thank you for your application. Applications are reviewed individually.</p>
          {reference ? <p className="ap-done__ref">Your reference is <strong>{reference}</strong>. Quote it if you need to contact us.</p> : null}
          <p className="ap-done__p">Selected candidates will be invited to a multi-stage recruitment process. Initial discussions focus on experience, judgement and mutual suitability. Detailed discussions regarding trading methodologies only occur later in the process, under appropriate confidentiality arrangements.</p>

          <div className="ap-track">
            {C.tracker.map((t, i) => (
              <div className={`ap-track__s${i === 0 ? " is-done" : ""}`} key={t}>
                <span className="ap-track__m"><Icon name="check" size={10} /></span>
                <span className="ap-track__l">{t}</span>
                <span className="ap-track__st">{i === 0 ? "Complete" : "Pending"}</span>
              </div>
            ))}
          </div>
        </div>
        <div className="ap-done__foot">
          <span>Aequitas Capital Partners LLP · London</span>
          <button className="cr-textlink" onClick={() => go("careers")}>Return to careers</button>
        </div>
      </div>
    </div>
  );
}

Object.assign(window, { ApplicationPage, SubmittedPage });
