/* Lightweight i18n — mirrors the react-i18next API (useTranslation, t, Trans).
   Picks up window.TRANSLATIONS (set by translations.js).

   Usage:
     const { t, lang, setLang } = useTranslation();
     t("header.name")                 -> "Arthur Thomas Facredyn"
     <Trans i18nKey="work.dosCavalos.b1" />  -> renders <b>/<i>/<a> tags

   Persists the chosen language in localStorage under "resume.lang".
*/

const I18nContext = React.createContext(null);

const STORAGE_KEY = "resume.lang";

function pathLang() {
  try {
    const p = window.location.pathname.replace(/\/+$/, "").toLowerCase();
    if (p === "/en") return "en";
    if (p === "/es") return "es";
  } catch (e) {}
  return null;
}

function getInitialLang() {
  // /en and /es path segments win — they are the canonical share URLs in prod.
  // ?lang= is next, used by the Puppeteer PDF builder to force a language
  // when rendering downloads/resume-en.pdf and downloads/resume-es.pdf.
  const fromPath = pathLang();
  if (fromPath) return fromPath;
  try {
    const urlLang = new URLSearchParams(window.location.search).get("lang");
    if (urlLang === "en" || urlLang === "es") return urlLang;
  } catch (e) {}
  try {
    const saved = localStorage.getItem(STORAGE_KEY);
    if (saved === "en" || saved === "es") return saved;
  } catch (e) {}
  return "en";
}

function getAtPath(obj, path) {
  return path.split(".").reduce((acc, k) => (acc == null ? acc : acc[k]), obj);
}

function I18nProvider({ children }) {
  const [lang, setLangState] = React.useState(getInitialLang);

  const setLang = React.useCallback((l) => {
    setLangState(l);
    try { localStorage.setItem(STORAGE_KEY, l); } catch (e) {}
    // Keep the URL in sync when we're on a lang-specific path (or the root),
    // so sharing/refreshing preserves the language. Leave other paths
    // (e.g. /Resume.html used by the Puppeteer PDF builder) untouched.
    try {
      const p = window.location.pathname.replace(/\/+$/, "").toLowerCase();
      if (p === "" || p === "/en" || p === "/es") {
        const next = "/" + l + window.location.search + window.location.hash;
        if (window.location.pathname !== "/" + l) {
          window.history.pushState({}, "", next);
        }
      }
    } catch (e) {}
  }, []);

  // Normalize uppercase /EN, /ES to lowercase on initial load, so shared
  // links stay canonical even if someone types the path in caps.
  React.useEffect(() => {
    try {
      const p = window.location.pathname;
      const lower = p.toLowerCase();
      if (p !== lower && (lower === "/en" || lower === "/es")) {
        window.history.replaceState({}, "", lower + window.location.search + window.location.hash);
      }
    } catch (e) {}
  }, []);

  // Back/forward should move between /en and /es without a reload.
  React.useEffect(() => {
    const onPop = () => {
      const fromPath = pathLang();
      if (fromPath && fromPath !== lang) setLangState(fromPath);
    };
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, [lang]);

  const t = React.useCallback((key, vars) => {
    const dict = (window.TRANSLATIONS && window.TRANSLATIONS[lang]) || {};
    let val = getAtPath(dict, key);
    if (val == null) {
      // fall back to English
      const en = (window.TRANSLATIONS && window.TRANSLATIONS.en) || {};
      val = getAtPath(en, key);
    }
    if (val == null) return key;
    if (typeof val === "string" && vars) {
      return val.replace(/\{\{(\w+)\}\}/g, (_, k) => (vars[k] != null ? vars[k] : `{{${k}}}`));
    }
    return val;
  }, [lang]);

  const value = React.useMemo(() => ({ lang, setLang, t }), [lang, setLang, t]);
  return <I18nContext.Provider value={value}>{children}</I18nContext.Provider>;
}

function useTranslation() {
  const ctx = React.useContext(I18nContext);
  if (!ctx) throw new Error("useTranslation must be used within I18nProvider");
  return ctx;
}

/* ---------- Inline-markup renderer ---------- */
/* Parses a tiny subset of HTML in translation strings: <b>, <i>, <a href="…">.
   This lets translators keep inline emphasis/links without raw dangerouslySetInnerHTML.
   Unknown tags are rendered as text. */

function parseMarkup(str) {
  if (typeof str !== "string") return [str];
  const out = [];
  const re = /<(\/?)(b|i|a)(\s+[^>]*)?>/gi;
  let lastIndex = 0;
  const stack = [{ tag: null, attrs: null, children: out }];

  let m;
  while ((m = re.exec(str)) !== null) {
    const [full, slash, tagRaw, attrsRaw] = m;
    const tag = tagRaw.toLowerCase();
    // text before this tag
    if (m.index > lastIndex) {
      stack[stack.length - 1].children.push(str.slice(lastIndex, m.index));
    }
    lastIndex = m.index + full.length;

    if (!slash) {
      // opening
      const attrs = {};
      if (attrsRaw) {
        const attrRe = /(\w+)\s*=\s*"([^"]*)"/g;
        let a;
        while ((a = attrRe.exec(attrsRaw)) !== null) attrs[a[1]] = a[2];
      }
      const node = { tag, attrs, children: [] };
      stack[stack.length - 1].children.push(node);
      stack.push(node);
    } else {
      // closing — pop until matching tag
      for (let i = stack.length - 1; i > 0; i--) {
        if (stack[i].tag === tag) {
          stack.length = i;
          break;
        }
      }
    }
  }
  if (lastIndex < str.length) {
    stack[stack.length - 1].children.push(str.slice(lastIndex));
  }

  const renderNodes = (nodes, keyPrefix = "") =>
    nodes.map((n, i) => {
      const k = keyPrefix + i;
      if (typeof n === "string") return n;
      const kids = renderNodes(n.children, k + ".");
      if (n.tag === "b") return <b key={k}>{kids}</b>;
      if (n.tag === "i") return <i key={k}>{kids}</i>;
      if (n.tag === "a") return <a key={k} href={n.attrs.href || "#"}>{kids}</a>;
      return <React.Fragment key={k}>{kids}</React.Fragment>;
    });

  return renderNodes(out);
}

function Trans({ i18nKey, vars }) {
  const { t } = useTranslation();
  const raw = t(i18nKey, vars);
  if (typeof raw !== "string") return <>{raw}</>;
  return <>{parseMarkup(raw)}</>;
}

/* ---------- Language pills ---------- */

/* iOS-style Translate glyph — the "文A" mark from Apple's Translate app:
   a CJK character on the left, a Latin "A" on the right, connected by a
   subtle arrow-like baseline. Drawn as thin strokes to match SF Symbols. */
/* iOS Translate-style mark: a bold CJK-ish glyph on top-left, a bold Latin
   "A" on bottom-right. Rendered with `fill` (not thin strokes) so it stays
   legible at small sizes, matching Apple's Translate app icon language. */
/* Lucide "Languages" icon (MIT). Depicts an "A" being translated to a
   different-script character — the standard translation glyph. */
const TranslateGlyph = () => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
    width="22"
    height="22"
    fill="none"
    stroke="currentColor"
    strokeWidth="2"
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden="true"
  >
    <path d="m5 8 6 6" />
    <path d="m4 14 6-6 2-3" />
    <path d="M2 5h12" />
    <path d="M7 2h1" />
    <path d="m22 22-5-10-5 10" />
    <path d="M14 18h6" />
  </svg>
);

function LanguagePills() {
  const { lang, setLang, t } = useTranslation();
  const langs = [
    { code: "en", label: t("lang.en") },
    { code: "es", label: t("lang.es") },
  ];
  return (
    <div className="lang-pills" role="group" aria-label="Language">
      <span className="lang-icon" aria-hidden="true"><TranslateGlyph /></span>
      {langs.map((l) => {
        const active = l.code === lang;
        return (
          <button
            key={l.code}
            type="button"
            className={"chip lang-chip" + (active ? " lang-chip--active" : "")}
            aria-pressed={active}
            onClick={() => setLang(l.code)}
          >
            <span>{l.label}</span>
          </button>
        );
      })}
    </div>
  );
}

/* ---------- Print button ---------- */

/* SF Symbols–style "printer" glyph: thin-stroke header sheet, printer body
   with a paper-out tray, and a small status dot. Matches the stroke weight
   of the other Cupertino-inspired icons used in this page. */
const PrinterGlyph = () => (
  <svg
    xmlns="http://www.w3.org/2000/svg"
    viewBox="0 0 24 24"
    width="16"
    height="16"
    fill="none"
    stroke="currentColor"
    strokeWidth="1.6"
    strokeLinecap="round"
    strokeLinejoin="round"
    aria-hidden="true"
  >
    <path d="M7 8.5V4h10v4.5" />
    <rect x="3.5" y="8.5" width="17" height="8" rx="2" />
    <rect x="7" y="13" width="10" height="7" rx="1" />
    <circle cx="17" cy="11.5" r="0.55" fill="currentColor" stroke="none" />
  </svg>
);

/* The "Print" button is a plain anchor that downloads a pre-rendered PDF
   from /downloads/resume-<lang>.pdf. Those PDFs are generated locally via
   `npm run build:pdf` (scripts/build-pdf.js uses Puppeteer). See CLAUDE.md.
   Keeping it as a real <a download> instead of window.print() gives us a
   vector PDF with clickable links and no Chrome header, and keeps the
   production deploy a pure static site. */
function PrintButton() {
  const { t, lang } = useTranslation();
  const href = `downloads/resume-${lang}.pdf`;
  const filename = (t("print.filename") || "Resume") + ".pdf";
  return (
    <a
      href={href}
      download={filename}
      className="chip print-btn"
      aria-label={t("print.aria")}
    >
      <PrinterGlyph />
      <span>{t("print.label")}</span>
    </a>
  );
}

Object.assign(window, { I18nProvider, I18nContext, useTranslation, Trans, LanguagePills, PrintButton, parseMarkup });
