/* feedback.jsx — SiteFooter + FeedbackModal.
 *
 * Browser-Babel runtime (no build). Loads before app.jsx so <SiteFooter /> resolves
 * at App() eval. Same i18n contract as the rest of the SPA: every visible string goes
 * through window.byokicuTr; both en and zh keys are added in strings.js to satisfy
 * tools/i18n-coverage.cjs.
 *
 * Form fields (all required): name, contact, suggestion (≤ 2000 chars). Submission
 * runs an invisible Turnstile render to mint a token (reusing window.BYOKICU_TURNSTILE_SITEKEY)
 * and POSTs { name, contact, suggestion, turnstileToken, pageUrl, userAgent } to
 * `/api/feedback`. The user worker forwards to the console via service binding; the
 * console verifies the token (TURNSTILE_SECRET) and fires DISCORD_WEBHOOK_URL.
 */

const { useState: useStateFb, useEffect: useEffectFb, useRef: useRefFb, useCallback: useCallbackFb } = React;
const trFb = window.byokicuTr;

const FEEDBACK_SUGGESTION_MAX = 2000;

function SiteFooter() {
  const [open, setOpen] = useStateFb(false);
  const year = new Date().getFullYear();
  return (
    <React.Fragment>
      <footer className="site-footer" role="contentinfo">
        <div className="site-footer-inner">
          <div className="site-footer-meta">
            <span className="site-footer-copy">© {year} byok.icu</span>
            <nav className="site-footer-links" aria-label={trFb("footer.linksAria")}>
              <a href="#" className="site-footer-link">{trFb("footer.privacy")}</a>
              <a href="#" className="site-footer-link">{trFb("footer.terms")}</a>
              <a href="#" className="site-footer-link">{trFb("footer.about")}</a>
            </nav>
          </div>
          <button
            type="button"
            className="site-footer-cta"
            onClick={() => setOpen(true)}
          >
            {trFb("footer.feedbackCta")}
          </button>
        </div>
      </footer>
      {open && <FeedbackModal onClose={() => setOpen(false)} />}
    </React.Fragment>
  );
}

function FeedbackModal({ onClose }) {
  const [name, setName] = useStateFb("");
  const [contact, setContact] = useStateFb("");
  const [suggestion, setSuggestion] = useStateFb("");
  const [submitting, setSubmitting] = useStateFb(false);
  const [errorMsg, setErrorMsg] = useStateFb("");
  const [successMsg, setSuccessMsg] = useStateFb("");
  const turnstileHostRef = useRefFb(null);
  const widgetIdRef = useRefFb(null);
  const tokenResolverRef = useRefFb(null);

  // Lock body scroll while open; restore on close.
  useEffectFb(() => {
    const prev = document.body.style.overflow;
    document.body.style.overflow = "hidden";
    return () => { document.body.style.overflow = prev; };
  }, []);

  // ESC closes — only when not mid-submit so we don't drop a token in flight.
  useEffectFb(() => {
    function onKey(e) { if (e.key === "Escape" && !submitting) onClose(); }
    window.addEventListener("keydown", onKey);
    return () => window.removeEventListener("keydown", onKey);
  }, [submitting, onClose]);

  // Render an invisible Turnstile widget once. siteverify happens server-side.
  // The widget's callback resolves a pending promise stored in tokenResolverRef.
  useEffectFb(() => {
    const sitekey = window.BYOKICU_TURNSTILE_SITEKEY;
    if (!sitekey || !turnstileHostRef.current) return;
    let cancelled = false;
    let poll;
    function render() {
      if (cancelled || !window.turnstile || !turnstileHostRef.current || widgetIdRef.current !== null) return;
      widgetIdRef.current = window.turnstile.render(turnstileHostRef.current, {
        sitekey,
        size: "invisible",
        callback: (token) => {
          const resolve = tokenResolverRef.current;
          tokenResolverRef.current = null;
          if (resolve) resolve(token);
        },
        "error-callback": () => {
          const resolve = tokenResolverRef.current;
          tokenResolverRef.current = null;
          if (resolve) resolve("");
        }
      });
    }
    if (window.turnstile) render();
    else poll = setInterval(() => { if (window.turnstile) { clearInterval(poll); render(); } }, 200);
    return () => {
      cancelled = true;
      if (poll) clearInterval(poll);
      if (widgetIdRef.current !== null && window.turnstile) {
        try { window.turnstile.remove(widgetIdRef.current); } catch (_) { /* ignore */ }
        widgetIdRef.current = null;
      }
    };
  }, []);

  const requestToken = useCallbackFb(() => {
    if (!window.turnstile || widgetIdRef.current === null) return Promise.resolve("");
    return new Promise((resolve) => {
      tokenResolverRef.current = resolve;
      try {
        window.turnstile.reset(widgetIdRef.current);
        window.turnstile.execute(widgetIdRef.current);
      } catch (_) {
        tokenResolverRef.current = null;
        resolve("");
      }
      // Fail-safe: if the widget never calls back within 12s, resolve empty so the
      // user gets an error message instead of a stuck spinner.
      setTimeout(() => {
        if (tokenResolverRef.current === resolve) {
          tokenResolverRef.current = null;
          resolve("");
        }
      }, 12000);
    });
  }, []);

  const nameTrim = name.trim();
  const contactTrim = contact.trim();
  const suggestionTrim = suggestion.trim();
  const canSubmit =
    !submitting &&
    nameTrim.length > 0 &&
    contactTrim.length > 0 &&
    suggestionTrim.length > 0 &&
    suggestionTrim.length <= FEEDBACK_SUGGESTION_MAX;

  async function onSubmit(e) {
    e.preventDefault();
    if (!canSubmit) return;
    setSubmitting(true);
    setErrorMsg("");
    setSuccessMsg("");
    try {
      const turnstileToken = await requestToken();
      if (!turnstileToken) {
        setErrorMsg(trFb("feedback.turnstileMissing"));
        setSubmitting(false);
        return;
      }
      const res = await fetch("/api/feedback", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          name: nameTrim,
          contact: contactTrim,
          suggestion: suggestionTrim,
          turnstileToken,
          pageUrl: window.location.href,
          userAgent: navigator.userAgent
        })
      });
      const data = await res.json().catch(() => ({}));
      if (!res.ok || !data || data.ok !== true) {
        setErrorMsg((data && data.error) || trFb("feedback.errorGeneric"));
        setSubmitting(false);
        return;
      }
      setSuccessMsg(trFb("feedback.success"));
      setName("");
      setContact("");
      setSuggestion("");
      setSubmitting(false);
      // auto-close after a beat so user sees the success state
      setTimeout(() => { onClose(); }, 1400);
    } catch (_) {
      setErrorMsg(trFb("feedback.errorGeneric"));
      setSubmitting(false);
    }
  }

  return (
    <div
      className="feedback-overlay"
      role="dialog"
      aria-modal="true"
      aria-labelledby="feedback-title"
      onClick={(e) => { if (e.target === e.currentTarget && !submitting) onClose(); }}
    >
      <div className="feedback-modal">
        <div className="feedback-header">
          <h2 id="feedback-title" className="feedback-title">{trFb("feedback.title")}</h2>
          <button
            type="button"
            className="feedback-close"
            onClick={onClose}
            disabled={submitting}
            aria-label={trFb("feedback.closeAria")}
          >×</button>
        </div>
        <p className="feedback-lede">{trFb("feedback.lede")}</p>
        <form className="feedback-form" onSubmit={onSubmit}>
          <label className="feedback-label">
            <span className="feedback-label-text">{trFb("feedback.name")} <em className="feedback-req">*</em></span>
            <input
              type="text"
              className="feedback-input"
              value={name}
              onChange={(e) => setName(e.target.value)}
              maxLength={200}
              required
              disabled={submitting}
            />
          </label>
          <label className="feedback-label">
            <span className="feedback-label-text">{trFb("feedback.contact")} <em className="feedback-req">*</em></span>
            <input
              type="text"
              className="feedback-input"
              value={contact}
              onChange={(e) => setContact(e.target.value)}
              placeholder={trFb("feedback.contactPh")}
              maxLength={400}
              required
              disabled={submitting}
            />
          </label>
          <label className="feedback-label">
            <span className="feedback-label-text">
              {trFb("feedback.suggestion")} <em className="feedback-req">*</em>
              <span className="feedback-count">{suggestionTrim.length}/{FEEDBACK_SUGGESTION_MAX}</span>
            </span>
            <textarea
              className="feedback-textarea"
              value={suggestion}
              onChange={(e) => setSuggestion(e.target.value.slice(0, FEEDBACK_SUGGESTION_MAX))}
              maxLength={FEEDBACK_SUGGESTION_MAX}
              rows={6}
              required
              disabled={submitting}
            />
          </label>
          <div ref={turnstileHostRef} className="feedback-turnstile-host" aria-hidden="true"></div>
          {errorMsg && <div className="feedback-error" role="alert">{errorMsg}</div>}
          {successMsg && <div className="feedback-success" role="status">{successMsg}</div>}
          <div className="feedback-actions">
            <button
              type="button"
              className="feedback-btn feedback-btn-secondary"
              onClick={onClose}
              disabled={submitting}
            >{trFb("feedback.cancel")}</button>
            <button
              type="submit"
              className="feedback-btn feedback-btn-primary"
              disabled={!canSubmit}
            >{submitting ? trFb("feedback.submitting") : trFb("feedback.submit")}</button>
          </div>
        </form>
      </div>
    </div>
  );
}
