/* hire-views.jsx — the Hire page (packages + consultation booking) and the
   reusable SubscribeForm used at the end of articles, Labs tutorials, and here.

   Money never touches this file's logic: the browser posts a service id (or an
   hour count) to the worker, which resolves the real price server-side and
   returns a Stripe Checkout URL. Nothing here can change what someone is charged.

   Depends on Reveal (views.jsx), i18n.js, services.js. */

const { useState: useHS, useEffect: useHE, useRef: useHRef } = React;

function sv(key, lang) {
  const ui = (window.SERVICES && window.SERVICES.ui) || {};
  const L = lang || window.SITE_LANG || "en";
  return (ui[L] && ui[L][key]) || (ui.en && ui.en[key]) || key;
}

function apiBase() {
  const c = window.HIRE_CONFIG || {};
  return (c.apiBase || "").replace(/\/$/, "");
}

function pickSvc(pkg, lang) {
  return pkg[lang] || pkg.en || pkg.ms || {};
}

const usd = (n) => `USD ${n.toLocaleString("en-US")}`;

/* ---------- Subscribe: a quiet inline form ---------- */
function SubscribeForm({ source, compact }) {
  const lang = useSiteLang();
  const [email, setEmail] = useHS("");
  const [state, setState] = useHS("idle");   // idle | sending | ok | already | error
  const [msg, setMsg] = useHS("");
  const hp = useHRef(null);                  // honeypot

  const submit = async (e) => {
    e.preventDefault();
    if (state === "sending") return;
    const v = email.trim();
    if (!v || !/^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/.test(v)) {
      setState("error"); setMsg(sv("sub_err", lang)); return;
    }
    setState("sending"); setMsg("");
    try {
      const res = await fetch(`${apiBase()}/api/subscribe`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          email: v, source: source || "site", language: lang,
          website: hp.current ? hp.current.value : "",
        }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok) { setState("error"); setMsg(data.error || sv("sub_err", lang)); return; }
      setState(data.already ? "already" : "ok");
      setMsg(data.already ? sv("sub_already", lang) : sv("sub_ok", lang));
      setEmail("");
      window.track && window.track("subscribe_submitted", { source: source || "site", language: lang });
    } catch {
      setState("error"); setMsg(sv("sub_err", lang));
    }
  };

  const done = state === "ok" || state === "already";

  return (
    <section className={`sub-box ${compact ? "is-compact" : ""}`}>
      {!compact && <h3 className="sub-head">{sv("sub_head", lang)}</h3>}
      <p className="sub-sub">{compact ? sv("sub_head", lang) : sv("sub_sub", lang)}</p>
      {done ? (
        <p className="sub-done">{msg}</p>
      ) : (
        <form className="sub-form" onSubmit={submit} noValidate>
          <input className="sub-input" type="email" inputMode="email" autoComplete="email"
            placeholder={sv("sub_ph", lang)} value={email}
            onChange={(e) => setEmail(e.target.value)} aria-label={sv("sub_head", lang)} />
          {/* honeypot — hidden from people, tempting to bots */}
          <input ref={hp} className="sub-hp" type="text" name="website" tabIndex="-1"
            autoComplete="off" aria-hidden="true" />
          <button className="sub-btn" type="submit" disabled={state === "sending"}>
            {sv("sub_cta", lang)}
          </button>
        </form>
      )}
      {state === "error" && <p className="sub-note is-error">{msg}</p>}
    </section>
  );
}

/* Shared checkout. Used by both the hire page and the product pages so there is
   exactly one code path to Stripe — the client posts an id, never an amount. */
function useCheckout(lang) {
  const [busy, setBusy] = useHS(null);
  const [err, setErr] = useHS("");
  const hp = useHRef(null);

  const checkout = async (payload, busyKey) => {
    setBusy(busyKey); setErr("");
    try {
      const res = await fetch(`${apiBase()}/api/checkout`, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ ...payload, website: hp.current ? hp.current.value : "" }),
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok || !data.url) {
        setErr(data.error || "Could not start checkout. Please try again.");
        setBusy(null); return;
      }
      window.track && window.track("checkout_started", { ...busyKeyProps(payload), language: lang });
      window.location.href = data.url;      // hand off to Stripe
    } catch {
      setErr("Could not start checkout. Please try again.");
      setBusy(null);
    }
  };

  return { busy, err, hp, checkout, buy: (pkg) => checkout({ kind: "service", serviceId: pkg.id }, pkg.id) };
}

/* ---------- a single product page ----------
   Reached from a package card. Nobody is asked to pay from the index: the card
   links here, and the buy button lives at the bottom of the itemised breakdown,
   after "not included" and the FAQ. Committing money should come after reading,
   not before. */
function ServiceView({ serviceId, onBack, onConsult }) {
  const lang = useSiteLang();
  const S = window.SERVICES || { packages: [] };
  const pkg = (S.packages || []).find((p) => p.id === serviceId);
  const d = (window.SERVICE_DETAILS || {})[serviceId];
  const { busy, err, hp, buy } = useCheckout(lang);

  useHE(() => {
    window.scrollTo({ top: 0, behavior: "auto" });
    if (pkg) window.track && window.track("service_viewed", { service_id: serviceId, language: lang });
  }, [serviceId]);

  if (!pkg) {
    return (
      <div className="view hire">
        <p className="hire-sub" style={{ paddingBlock: "2rem" }}>
          That package could not be found.{" "}
          <button className="back-link" onClick={onBack}>{sv("back_hire", lang)}</button>
        </p>
      </div>
    );
  }

  const v = pickSvc(pkg, lang);
  const buyLabel = `${sv("buy", lang)} · ${usd(pkg.priceUsd)}`;

  return (
    <div className="view service-page">
      <Reveal as="nav" className="entry-back">
        <button className="back-link" onClick={onBack}>{sv("back_hire", lang)}</button>
      </Reveal>

      <Reveal as="header" className="svc-head" delay={50}>
        <div className="svc-icon" aria-hidden="true">{pkg.icon}</div>
        <h1 className="svc-title">{v.name}</h1>
        {d && <p className="svc-tagline">{d.tagline}</p>}
        <div className="svc-price-row">
          <span className="svc-price">{usd(pkg.priceUsd)}</span>
          {d && d.timeline && <span className="svc-timeline">{d.timeline}</span>}
        </div>
      </Reveal>

      {!d && (
        <Reveal as="div" className="svc-section" delay={70}>
          <p className="svc-blurb">{v.blurb}</p>
          <ul className="svc-list">
            {(v.points || []).map((pt, i) => <li key={i}>{pt}</li>)}
          </ul>
        </Reveal>
      )}

      {d && (
        <>
          {/* who it is and isn't for — stated before anything else */}
          <Reveal as="section" className="svc-section svc-fit" delay={70}>
            <div className="svc-fit-col">
              <h2 className="svc-h3 is-yes">Right for you if</h2>
              <ul className="svc-list is-yes">
                {d.bestFor.map((x, i) => <li key={i}>{x}</li>)}
              </ul>
            </div>
            <div className="svc-fit-col">
              <h2 className="svc-h3 is-no">Not this, if</h2>
              <ul className="svc-list is-no">
                {d.notFor.map((x, i) => <li key={i}>{x}</li>)}
              </ul>
            </div>
          </Reveal>

          <Reveal as="section" className="svc-section" delay={80}>
            <h2 className="svc-h2">What the {usd(pkg.priceUsd)} covers</h2>
            <p className="svc-note">
              Every line below is included in the price. Nothing here is an upsell.
            </p>
            <dl className="svc-incl">
              {d.included.map((x, i) => (
                <div className="svc-incl-row" key={i}>
                  <dt><span className="svc-tick" aria-hidden="true">✓</span>{x.item}</dt>
                  <dd>{x.detail}</dd>
                </div>
              ))}
            </dl>
          </Reveal>

          <Reveal as="section" className="svc-section" delay={80}>
            <h2 className="svc-h2">How it runs</h2>
            <ol className="svc-process">
              {d.process.map((s, i) => (
                <li key={i}>
                  <span className="svc-step-n" aria-hidden="true">{i + 1}</span>
                  <div>
                    <p className="svc-step-label">{s.label}</p>
                    <p className="svc-step-detail">{s.detail}</p>
                  </div>
                </li>
              ))}
            </ol>
          </Reveal>

          {/* deliberately as prominent as the inclusions */}
          <Reveal as="section" className="svc-section" delay={80}>
            <h2 className="svc-h2">Not included</h2>
            <p className="svc-note">
              Said plainly up front, because unexpected exclusions are what sour a project.
            </p>
            <dl className="svc-excl">
              {d.notIncluded.map((x, i) => (
                <div className="svc-excl-row" key={i}>
                  <dt><span className="svc-cross" aria-hidden="true">—</span>{x.item}</dt>
                  <dd>{x.note}</dd>
                </div>
              ))}
            </dl>
          </Reveal>

          {d.addOns && d.addOns.length > 0 && (
            <Reveal as="section" className="svc-section" delay={80}>
              <h2 className="svc-h2">Add-ons</h2>
              <p className="svc-note">Priced now so you can budget, not quoted later.</p>
              <ul className="svc-addons">
                {d.addOns.map((a, i) => (
                  <li key={i}>
                    <span className="svc-addon-name">{a.item}</span>
                    <span className="svc-addon-price">{a.price}</span>
                    {a.note && <span className="svc-addon-note">{a.note}</span>}
                  </li>
                ))}
              </ul>
            </Reveal>
          )}

          <Reveal as="section" className="svc-section svc-two" delay={80}>
            <div>
              <h2 className="svc-h2">What I need from you</h2>
              <ul className="svc-list">
                {d.youProvide.map((x, i) => <li key={i}>{x}</li>)}
              </ul>
            </div>
            <div>
              <h2 className="svc-h2">Payment</h2>
              <p className="svc-payment">{d.payment}</p>
            </div>
          </Reveal>

          {d.faq && d.faq.length > 0 && (
            <Reveal as="section" className="svc-section" delay={80}>
              <h2 className="svc-h2">Questions</h2>
              <div className="svc-faq">
                {d.faq.map((f, i) => (
                  <details className="svc-faq-item" key={i}>
                    <summary>{f.q}</summary>
                    <p>{f.a}</p>
                  </details>
                ))}
              </div>
            </Reveal>
          )}
        </>
      )}

      {/* the commitment, at the END */}
      <Reveal as="section" className="svc-buy" delay={90}>
        {err && <p className="hire-error" role="alert">{err}</p>}
        <input ref={hp} className="sub-hp" type="text" name="website" tabIndex="-1"
          autoComplete="off" aria-hidden="true" />
        <div className="svc-buy-inner">
          <div>
            <p className="svc-buy-name">{v.name}</p>
            <p className="svc-buy-price">{usd(pkg.priceUsd)}</p>
          </div>
          <button className="svc-buy-btn" onClick={() => buy(pkg)} disabled={busy === pkg.id}>
            {busy === pkg.id ? sv("submitting", lang) : buyLabel}
          </button>
        </div>
        <p className="svc-buy-note">
          {sv("svc_unsure", lang)}{" "}
          <button className="svc-inline-link" onClick={onConsult}>
            {sv("svc_book_instead", lang)}
          </button>
        </p>
      </Reveal>

      <Reveal as="footer" className="hire-foot"><span className="foot-mark">了</span></Reveal>
    </div>
  );
}

/* ---------- Hire page ---------- */
function HireView({ onOpenService }) {
  const lang = useSiteLang();
  const S = window.SERVICES || { packages: [], consult: {} };
  const C = S.consult || {};
  const { busy, err, hp, checkout } = useCheckout(lang);

  // consultation form
  const [f, setF] = useHS({
    name: "", email: "", topic: "web", message: "", hours: 1,
    preferredTimes: "", timezone: guessTz(),
  });
  const set = (k) => (e) => setF((s) => ({ ...s, [k]: e.target.value }));

  useHE(() => { window.scrollTo({ top: 0, behavior: "auto" }); }, []);

  const book = (e) => {
    e.preventDefault();
    if (busy) return;
    checkout({ kind: "consult", ...f, hours: Number(f.hours) || 1 }, "consult");
  };

  const hours = Number(f.hours) || 1;
  const listTotal = (C.rateUsd || 100) * hours;

  return (
    <div className="view hire">
      <Reveal as="header" className="hire-head">
        <h1 className="hire-title">{sv("hire_title", lang)}</h1>
        <p className="hire-sub">{sv("hire_sub", lang)}</p>
      </Reveal>

      {err && <p className="hire-error" role="alert">{err}</p>}
      {/* shared honeypot for both flows */}
      <input ref={hp} className="sub-hp" type="text" name="website" tabIndex="-1"
        autoComplete="off" aria-hidden="true" />

      {/* ---- packages ---- */}
      <Reveal as="section" className="hire-section" delay={60}>
        <h2 className="hire-h2">{sv("packages_head", lang)}</h2>
        <p className="hire-note">{sv("packages_sub", lang)}</p>

        <div className="pkg-grid">
          {(S.packages || []).map((p) => {
            const v = pickSvc(p, lang);
            const det = (window.SERVICE_DETAILS || {})[p.id];
            const n = det ? det.included.length : 0;
            return (
              <article className="pkg-card is-clickable" key={p.id}
                onClick={() => onOpenService(p.id)}>
                <div className="pkg-icon" aria-hidden="true">{p.icon}</div>
                <h3 className="pkg-name">{v.name}</h3>
                <p className="pkg-price">{usd(p.priceUsd)}</p>
                <p className="pkg-blurb">{v.blurb}</p>
                <ul className="pkg-points">
                  {(v.points || []).map((pt, i) => <li key={i}>{pt}</li>)}
                </ul>
                {/* the card sends you to the breakdown — never straight to payment */}
                <button className="pkg-buy"
                  onClick={(e) => { e.stopPropagation(); onOpenService(p.id); }}>
                  {sv("see_details", lang)}
                  {n > 0 && <span className="pkg-buy-count"> · {n} {sv("items_included", lang)}</span>}
                </button>
              </article>
            );
          })}
        </div>
      </Reveal>

      {/* ---- consultation ---- */}
      <Reveal as="section" className="hire-section" delay={90}>
        <h2 className="hire-h2">{sv("consult_head", lang)}</h2>
        <p className="hire-note">{sv("consult_sub", lang)}</p>

        <div className="consult-rate">
          <span className="rate-was">{usd(C.rateUsd || 100)}</span>
          <span className="rate-now">{usd(C.earlyBirdUsd || 50)}</span>
          <span className="rate-per">{sv("per_hour", lang)}</span>
          <span className="rate-tag">{sv("early_bird", lang)}</span>
        </div>
        <p className="rate-note">
          {sv("early_bird_note", lang).replace("{code}", C.promoCode || "EARLYBIRD")}
        </p>

        <form className="consult-form" onSubmit={book}>
          <div className="cf-row">
            <label className="cf-field">
              <span className="cf-label">{sv("form_name", lang)}</span>
              <input className="cf-input" value={f.name} onChange={set("name")} required maxLength={120} />
            </label>
            <label className="cf-field">
              <span className="cf-label">{sv("form_email", lang)}</span>
              <input className="cf-input" type="email" inputMode="email" value={f.email}
                onChange={set("email")} required maxLength={254} />
            </label>
          </div>

          <div className="cf-row">
            <label className="cf-field">
              <span className="cf-label">{sv("form_topic", lang)}</span>
              <select className="cf-input" value={f.topic} onChange={set("topic")}>
                <option value="web">{sv("topic_web", lang)}</option>
                <option value="app">{sv("topic_app", lang)}</option>
                <option value="automation">{sv("topic_automation", lang)}</option>
                <option value="other">{sv("topic_other", lang)}</option>
              </select>
            </label>
            <label className="cf-field cf-narrow">
              <span className="cf-label">{sv("form_hours", lang)}</span>
              <select className="cf-input" value={f.hours} onChange={set("hours")}>
                {Array.from({ length: C.maxHours || 8 }, (_, i) => i + 1).map((h) => (
                  <option key={h} value={h}>
                    {h} {h === 1 ? sv("hour", lang) : sv("hours", lang)}
                  </option>
                ))}
              </select>
            </label>
          </div>

          <label className="cf-field">
            <span className="cf-label">{sv("form_message", lang)}</span>
            <textarea className="cf-input cf-textarea" value={f.message} onChange={set("message")}
              required minLength={10} maxLength={4000} rows={4} />
          </label>

          <div className="cf-row">
            <label className="cf-field">
              <span className="cf-label">{sv("form_times", lang)}</span>
              <input className="cf-input" value={f.preferredTimes} onChange={set("preferredTimes")}
                placeholder={sv("form_times_ph", lang)} maxLength={500} />
            </label>
            <label className="cf-field cf-narrow">
              <span className="cf-label">{sv("form_tz", lang)}</span>
              <input className="cf-input" value={f.timezone} onChange={set("timezone")} maxLength={64} />
            </label>
          </div>

          <div className="cf-foot">
            <div className="cf-total">
              <span className="cf-total-label">{sv("total", lang)}</span>
              <span className="cf-total-val">{usd(listTotal)}</span>
              <span className="cf-total-hint">
                {sv("early_bird_note", lang).replace("{code}", C.promoCode || "EARLYBIRD")}
              </span>
            </div>
            <button className="cf-submit" type="submit" disabled={busy === "consult"}>
              {busy === "consult" ? sv("submitting", lang) : sv("book", lang)}
            </button>
          </div>
          <p className="cf-note">{sv("pay_note", lang)}</p>
        </form>
      </Reveal>

      <Reveal as="div" className="hire-section" delay={110}>
        <SubscribeForm source="hire" />
      </Reveal>

      <Reveal as="footer" className="hire-foot"><span className="foot-mark">了</span></Reveal>
    </div>
  );
}

/* ---------- post-checkout thank-you ---------- */
function HireThanksView({ onBack }) {
  const lang = useSiteLang();
  const ref = new URLSearchParams(location.search).get("ref") || "";
  useHE(() => {
    window.scrollTo({ top: 0, behavior: "auto" });
    window.track && window.track("checkout_completed", { ref, language: lang });
  }, []);
  return (
    <div className="view hire">
      <Reveal as="div" className="thanks-box">
        <h1 className="hire-title">{sv("thanks_title", lang)}</h1>
        <p className="hire-sub">{sv("thanks_body", lang)}</p>
        <p className="thanks-pending">{sv("thanks_pending", lang)}</p>
        {ref && <p className="thanks-ref">{ref}</p>}
        <button className="back-link" onClick={onBack}>{sv("back_hire", lang)}</button>
      </Reveal>
    </div>
  );
}

function guessTz() {
  try { return Intl.DateTimeFormat().resolvedOptions().timeZone || ""; } catch { return ""; }
}
function busyKeyProps(payload) {
  return payload.kind === "service"
    ? { kind: "service", service_id: payload.serviceId }
    : { kind: "consult", topic: payload.topic, hours: payload.hours };
}

Object.assign(window, { HireView, HireThanksView, ServiceView, SubscribeForm });
