// Program builder — drag classical exercises into a saved, named program.
// Mounts to #program-root. Programs persist to the signed-in user's Clerk account.
(function () {
  const { useState, useEffect, useRef, useMemo } = React;

  // Language helpers
  function useLang() {
    const [lang, setLang] = useState(window.PilatesI18n?.getLang() || "en");
    useEffect(() => window.PilatesI18n?.subscribe(setLang), []);
    return lang;
  }
  function tt(key, vars) { return window.PilatesI18n?.t(key, vars) || key; }

  // parse "3m" / "~2m" → minutes
  function durMin(d) {
    const m = /(\d+)\s*m/.exec(d || "");
    return m ? parseInt(m[1], 10) : 0;
  }

  // Posture-focus filter — each posture / leg / foot pattern maps to the
  // muscle groups it most needs trained. An exercise matches if it recruits any.
  const POSTURE_TARGETS = {
    kypholordosis: ["trapezius", "rhomboid", "serratus", "transversus_abdominis", "oblique", "internal_oblique", "multifidus", "glute", "hamstring"],
    flatback:      ["iliopsoas", "erector", "trapezius", "rhomboid", "multifidus"],
    swayback:      ["trapezius", "rhomboid", "glute_med", "oblique", "transversus_abdominis"],
    varum:         ["glute", "piriformis", "tib", "peroneus"],
    valgum:        ["glute_med", "glute", "piriformis", "adductor"],
    recurvatum:    ["hamstring", "calf", "quad"],
    supination:    ["peroneus", "calf"],
    pronation:     ["tib", "peroneus", "glute"],
  };
  const POSTURE_GROUPS = [
    { label: "pg.posture.type", opts: [["kypholordosis", "pg.posture.kypholordosis"], ["flatback", "pg.posture.flatback"], ["swayback", "pg.posture.swayback"]] },
    { label: "pg.posture.legs", opts: [["varum", "pg.posture.varum"], ["valgum", "pg.posture.valgum"], ["recurvatum", "pg.posture.recurvatum"]] },
    { label: "pg.posture.feet", opts: [["supination", "pg.posture.supination"], ["pronation", "pg.posture.pronation"]] },
  ];

  function openDetail(id) {
    if (window.PilatesModal) window.PilatesModal.open("exercise", { id: id });
  }

  function ProgramBuilder() {
    useLang();
    const ALL = (window.PilatesAnatomy && window.PilatesAnatomy.EXERCISES) || [];
    const byId = useMemo(() => {
      const m = {}; ALL.forEach(e => { m[e.id] = e; }); return m;
    }, []);
    const apparatus = useMemo(() => {
      const s = []; ALL.forEach(e => { if (e.apparatus && !s.includes(e.apparatus)) s.push(e.apparatus); });
      return s;
    }, []);
    const muscleOpts = useMemo(() => {
      const seen = {};
      ALL.forEach(e => (e.muscles || []).forEach(m => { seen[m] = true; }));
      const M = (window.PilatesAnatomy && window.PilatesAnatomy.MUSCLES) || {};
      return Object.keys(seen)
        .map(id => ({ id: id, label: (M[id] && M[id].label) || id }))
        .sort((a, b) => a.label.localeCompare(b.label));
    }, []);

    const [search, setSearch] = useState("");
    const [appFilter, setAppFilter] = useState("All");
    const [muscleFilter, setMuscleFilter] = useState("");
    const [postureFilter, setPostureFilter] = useState("");
    const [name, setName] = useState("");
    const [items, setItems] = useState([]);          // ordered exercise ids
    const [user, setUser] = useState(null);
    const [saved, setSaved] = useState([]);          // [{id,name,exercises,updated}]
    const [currentId, setCurrentId] = useState("");  // "" = unsaved/new
    const [status, setStatus] = useState("");
    const [statusOk, setStatusOk] = useState(false);
    const [saving, setSaving] = useState(false);
    const [over, setOver] = useState(false);
    const [dropIdx, setDropIdx] = useState(-1);
    const [mobileTab, setMobileTab] = useState("library");
    const dragFrom = useRef(null);                   // index when reordering

    function flash(msg, ok) { setStatus(msg); setStatusOk(!!ok); }

    // ── Clerk sync ──
    useEffect(() => {
      let unsub;
      if (!window.clerkReady) return undefined;
      window.clerkReady.then(clerk => {
        const sync = () => {
          setUser(clerk.user || null);
          const ps = clerk.user && clerk.user.unsafeMetadata && clerk.user.unsafeMetadata.programs;
          setSaved(Array.isArray(ps) ? ps : []);
        };
        sync();
        unsub = clerk.addListener(sync);
      }).catch(() => {});
      return () => { if (unsub) unsub(); };
    }, []);

    // ── external load (clicked from the member-name dropdown) ──
    useEffect(() => {
      const handler = (e) => loadProgram(e.detail);
      window.addEventListener("pilates:load-program", handler);
      return () => window.removeEventListener("pilates:load-program", handler);
    }, []);

    function liveSaved() {
      const ps = window.Clerk && window.Clerk.user && window.Clerk.user.unsafeMetadata
        && window.Clerk.user.unsafeMetadata.programs;
      return Array.isArray(ps) ? ps : saved;
    }

    function loadProgram(id) {
      const p = liveSaved().find(x => x.id === id);
      if (!p) return;
      setCurrentId(p.id);
      setName(p.name || "");
      setItems((p.exercises || []).filter(eid => byId[eid]));
      flash(tt("pg.msg.loaded", { name: p.name || "program" }), true);
    }
    function newProgram() { setCurrentId(""); setName(""); setItems([]); flash("", false); }

    function addExercise(id) {
      if (byId[id]) {
        setItems(prev => [...prev, id]);
        if (window.innerWidth <= 820) setMobileTab("program");
      }
    }
    function removeAt(i) { setItems(prev => prev.filter((_, idx) => idx !== i)); }

    // ── drag-and-drop ──
    function onLibDragStart(e, id) {
      e.dataTransfer.setData("application/x-pg-exid", id);
      e.dataTransfer.effectAllowed = "copy";
    }
    function onItemDragStart(e, i) {
      dragFrom.current = i;
      e.dataTransfer.effectAllowed = "move";
      e.dataTransfer.setData("application/x-pg-move", String(i));
    }
    function onZoneDrop(e) {
      e.preventDefault();
      setOver(false); setDropIdx(-1);
      const exid = e.dataTransfer.getData("application/x-pg-exid");
      if (exid) addExercise(exid);
      dragFrom.current = null;
    }
    function onItemDrop(e, i) {
      e.preventDefault(); e.stopPropagation();
      setOver(false); setDropIdx(-1);
      const exid = e.dataTransfer.getData("application/x-pg-exid");
      if (exid) {
        setItems(prev => { const n = prev.slice(); n.splice(i, 0, exid); return n; });
        return;
      }
      const from = dragFrom.current;
      dragFrom.current = null;
      if (from == null || from === i) return;
      setItems(prev => {
        const n = prev.slice();
        const [moved] = n.splice(from, 1);
        n.splice(from < i ? i - 1 : i, 0, moved);
        return n;
      });
    }

    // ── persistence (Clerk unsafeMetadata) ──
    async function save() {
      const clerk = window.clerkReady ? await window.clerkReady : null;
      if (!clerk || !clerk.user) {
        flash(tt("pg.msg.signin"), false);
        if (window.openAuth) window.openAuth("signup");
        return;
      }
      if (!items.length) { flash(tt("pg.msg.addone"), false); return; }
      setSaving(true);
      try {
        const meta = clerk.user.unsafeMetadata || {};
        const programs = Array.isArray(meta.programs) ? meta.programs.slice() : [];
        const prog = {
          id: currentId || ("p_" + Date.now()),
          name: name.trim() || tt("pg.untitled"),
          exercises: items,
          updated: new Date().toISOString(),
        };
        const idx = programs.findIndex(p => p.id === prog.id);
        if (idx >= 0) programs[idx] = prog; else programs.push(prog);
        await clerk.user.update({ unsafeMetadata: Object.assign({}, meta, { programs }) });
        setSaved(programs);
        setCurrentId(prog.id);
        flash(tt("pg.msg.saved", { name: prog.name }), true);
        window.dispatchEvent(new CustomEvent("pilates:programs-changed"));
      } catch (err) {
        flash(tt("pg.msg.savefail"), false);
      } finally { setSaving(false); }
    }
    async function deleteCurrent() {
      const clerk = window.clerkReady ? await window.clerkReady : null;
      if (!clerk || !clerk.user || !currentId) return;
      setSaving(true);
      try {
        const meta = clerk.user.unsafeMetadata || {};
        const programs = (Array.isArray(meta.programs) ? meta.programs : []).filter(p => p.id !== currentId);
        await clerk.user.update({ unsafeMetadata: Object.assign({}, meta, { programs }) });
        setSaved(programs);
        newProgram();
        flash(tt("pg.msg.deleted"), true);
        window.dispatchEvent(new CustomEvent("pilates:programs-changed"));
      } catch (err) {
        flash(tt("pg.msg.deletefail"), false);
      } finally { setSaving(false); }
    }

    // ── derived library list ──
    const libList = useMemo(() => {
      const q = search.trim().toLowerCase();
      const postureSet = postureFilter ? POSTURE_TARGETS[postureFilter] : null;
      return ALL.filter(e => {
        if (appFilter !== "All" && e.apparatus !== appFilter) return false;
        if (muscleFilter && !(e.muscles || []).includes(muscleFilter)) return false;
        if (postureSet && !(e.muscles || []).some(m => postureSet.indexOf(m) >= 0)) return false;
        if (q && !((e.name || "").toLowerCase().includes(q)
          || (e.id || "").toLowerCase().includes(q)
          || (e.focus || "").toLowerCase().includes(q))) return false;
        return true;
      });
    }, [search, appFilter, muscleFilter, postureFilter]);

    const totalMin = items.reduce((s, id) => s + durMin(byId[id] && byId[id].duration), 0);

    return (
      <div className="pg-wrap">

        {/* toolbar */}
        <div className="pg-toolbar">
          <input className="pg-name-input" placeholder={tt("pg.name.placeholder")}
            value={name} maxLength={60} onChange={e => setName(e.target.value)} />
          {user && saved.length > 0 && (
            <select className="pg-select" value={currentId}
              onChange={e => { const v = e.target.value; if (v) loadProgram(v); else newProgram(); }}>
              <option value="">{tt("pg.newprogram")}</option>
              {saved.map(p => <option key={p.id} value={p.id}>{p.name}</option>)}
            </select>
          )}
          <button className="pg-btn" onClick={newProgram}>{tt("pg.new")}</button>
          {currentId && <button className="pg-btn" onClick={deleteCurrent} disabled={saving}>{tt("pg.delete")}</button>}
          <button className="pg-btn solid" onClick={save} disabled={saving}>
            {saving ? tt("pg.saving") : (currentId ? tt("pg.update") : tt("pg.save"))}
          </button>
        </div>

        {!user && (
          <div className="pg-signin-note">
            {tt("pg.signin.note.a")}<strong>{tt("pg.signin.note.save")}</strong>{tt("pg.signin.note.b")}
            <button onClick={() => window.openAuth && window.openAuth("signup")}>{tt("pg.signin.note.link")}</button>
          </div>
        )}

        {/* mobile tab switcher */}
        <div className="pg-tabs">
          <button className={"pg-tab" + (mobileTab === "library" ? " active" : "")}
            onClick={() => setMobileTab("library")}>
            {tt("pg.library")} <span className="pg-tab-count">{libList.length}</span>
          </button>
          <button className={"pg-tab" + (mobileTab === "program" ? " active" : "")}
            onClick={() => setMobileTab("program")}>
            {name.trim() || tt("pg.yourprogram")} <span className="pg-tab-count">{items.length}</span>
          </button>
        </div>

        {/* two panels */}
        <div className="pg-grid">

          {/* library */}
          <div className={"pg-panel" + (mobileTab !== "library" ? " pg-panel--hidden" : "")}>
            <div className="pg-panel-head">
              <span>{tt("pg.library")}</span>
              <span className="pg-count">{libList.length}</span>
            </div>
            <input className="pg-search" placeholder={tt("pg.search.placeholder")}
              value={search} onChange={e => setSearch(e.target.value)} />
            <div className="pg-filters">
              <select className="pg-fselect" value={appFilter}
                onChange={e => setAppFilter(e.target.value)}>
                <option value="All">{tt("pg.filter.allapp")}</option>
                {apparatus.map(a => <option key={a} value={a}>{a}</option>)}
              </select>
              <select className="pg-fselect" value={muscleFilter}
                onChange={e => setMuscleFilter(e.target.value)}>
                <option value="">{tt("pg.filter.allmuscle")}</option>
                {muscleOpts.map(m => <option key={m.id} value={m.id}>{m.label}</option>)}
              </select>
              <select className="pg-fselect" value={postureFilter}
                onChange={e => setPostureFilter(e.target.value)}>
                <option value="">{tt("pg.filter.allposture")}</option>
                {POSTURE_GROUPS.map(g => (
                  <optgroup key={g.label} label={tt(g.label)}>
                    {g.opts.map(o => <option key={o[0]} value={o[0]}>{tt(o[1])}</option>)}
                  </optgroup>
                ))}
              </select>
            </div>
            <div className="pg-lib-list">
              {libList.map(e => (
                <div key={e.id} className="pg-ex-card" draggable
                  onDragStart={ev => onLibDragStart(ev, e.id)}
                  onClick={() => openDetail(e.id)}
                  title={tt("pg.title.cues")}>
                  <span className="pg-ex-id">{e.id}</span>
                  <span className="pg-ex-name">{e.name}</span>
                  <span className="pg-ex-meta">{e.apparatus}<br />{e.duration || ""}</span>
                  <button className="pg-ex-add" title={tt("pg.title.add")}
                    onClick={ev => { ev.stopPropagation(); addExercise(e.id); }}>+</button>
                </div>
              ))}
              {libList.length === 0 && (
                <div className="pg-empty" style={{ minHeight: 120 }}>
                  <span className="pg-empty-text">{tt("pg.empty.nomatch")}</span>
                </div>
              )}
            </div>
          </div>

          {/* builder */}
          <div className={"pg-panel" + (mobileTab !== "program" ? " pg-panel--hidden" : "")}>
            <div className="pg-panel-head">
              <span className="pg-name-echo">{name.trim() || tt("pg.yourprogram")}</span>
              <span className="pg-count">{items.length}</span>
            </div>
            <div className={"pg-drop" + (over ? " over" : "")}
              onDragOver={e => { e.preventDefault(); setOver(true); }}
              onDragLeave={e => { if (e.currentTarget === e.target) setOver(false); }}
              onDrop={onZoneDrop}>
              {items.length === 0 && (
                <div className={"pg-empty" + (over ? " over" : "")}>
                  <span className="pg-empty-mark">⊕</span>
                  <span className="pg-empty-text">{tt("pg.drop.here")}</span>
                  <span className="pg-empty-sub">{tt("pg.drop.sub")}</span>
                </div>
              )}
              {items.map((id, i) => {
                const e = byId[id] || { name: id, apparatus: "", duration: "" };
                return (
                  <div key={i + "_" + id}
                    className={"pg-item" + (dropIdx === i ? " drop-before" : "")}
                    draggable
                    onDragStart={ev => onItemDragStart(ev, i)}
                    onDragOver={ev => { ev.preventDefault(); setDropIdx(i); }}
                    onDragLeave={() => setDropIdx(-1)}
                    onDrop={ev => onItemDrop(ev, i)}>
                    <span className="pg-item-num">{String(i + 1).padStart(2, "0")}</span>
                    <span className="pg-item-handle">⠿</span>
                    <span>
                      <span className="pg-item-name" title={tt("pg.title.cues")}
                        onClick={() => openDetail(id)}>{e.name}</span>
                      <span className="pg-item-meta">
                        {e.apparatus}{e.duration ? " · " + e.duration : ""}
                      </span>
                    </span>
                    <button className="pg-item-remove" title={tt("pg.title.remove")}
                      onClick={() => removeAt(i)}>×</button>
                  </div>
                );
              })}
            </div>
            <div className="pg-summary">
              <span><strong>{items.length}</strong>&nbsp; {tt("pg.summary.exercises")}</span>
              <span>≈ <strong>{totalMin}</strong>&nbsp; {tt("pg.summary.min")}</span>
            </div>
          </div>

        </div>

        <div className={"pg-status" + (statusOk ? " ok" : "")}>{status}</div>
      </div>
    );
  }

  window.ProgramBuilder = ProgramBuilder;
  const root = document.getElementById("program-root");
  if (root) ReactDOM.createRoot(root).render(<ProgramBuilder />);
})();
