/* planner-app.jsx — top-level app shell */

(function () {
  const {
    DAY_NAMES, DAY_SHORT, PALETTE, BLOCK_TYPES,
    startOfWeek, addDays, toISODate, parseISODate, fmtDate, fmtDur,
    normalizeBlock, loadState, saveState, normalizeState, sampleBlocks, planCompletion,
    makeId, clamp, snapMin
  } = window.PLANNER_DATA;

  function App() {
    const [state, setState] = React.useState(loadState);
    const [teacher, setTeacher] = React.useState(null);
    const [serverReady, setServerReady] = React.useState(false);
    const [authMode, setAuthMode] = React.useState("login");
    const [authForm, setAuthForm] = React.useState({ name: "", email: "", password: "" });
    const [authError, setAuthError] = React.useState("");
    const [syncStatus, setSyncStatus] = React.useState("Checking account...");
    const [activeId, setActiveId] = React.useState(null);
    const [editorOpen, setEditorOpen] = React.useState(false);
    const [toast, setToast] = React.useState(null);
    const [confetti, setConfetti] = React.useState([]);
    const [tweaks, setTweaks] = React.useState(() => window.PLANNER_TWEAK_DEFAULTS);

    /* Pick up tweak changes (since useTweaks is in PlannerTweaks; we mirror via postMessage) */
    React.useEffect(() => {
      function onMsg(e) {
        const d = e.data;
        if (d && d.type === "__edit_mode_set_keys" && d.edits) {
          setTweaks(prev => ({ ...prev, ...d.edits }));
        }
      }
      window.addEventListener("message", onMsg);
      return () => window.removeEventListener("message", onMsg);
    }, []);

    /* Apply theme to document */
    React.useEffect(() => {
      document.body.setAttribute("data-theme", tweaks.theme);
      document.body.setAttribute("data-motion", tweaks.motion < 3 ? "low" : "normal");
    }, [tweaks.theme, tweaks.motion]);

    React.useEffect(() => {
      let cancelled = false;
      async function boot() {
        try {
          const me = await apiFetch("/api/auth/me");
          if (cancelled) return;
          setTeacher(me.teacher);
          await loadPlannerFromServer({ fallbackState: state, setState, setSyncStatus });
          if (!cancelled) {
            setServerReady(true);
          }
        } catch (error) {
          if (!cancelled) {
            setTeacher(false);
            setServerReady(false);
            setSyncStatus("Sign in to sync");
          }
        }
      }
      boot();
      return () => { cancelled = true; };
    }, []);

    /* Persist locally for fast reloads and remotely for signed-in teachers. */
    React.useEffect(() => {
      saveState(state);
      if (!teacher || !serverReady) return;

      setSyncStatus("Saving...");
      clearTimeout(window.__plannerSaveTimer);
      window.__plannerSaveTimer = setTimeout(async () => {
        try {
          await apiFetch("/api/planner", {
            method: "PUT",
            body: JSON.stringify({ state })
          });
          setSyncStatus("Saved to Cloudflare");
        } catch (error) {
          setSyncStatus("Save failed");
        }
      }, 650);
    }, [state, teacher, serverReady]);

    const currentWeekStart = state.currentWeekStart;
    const weekStartDate = parseISODate(currentWeekStart);
    const week = state.weeks[currentWeekStart] || { settings: { ...window.PLANNER_DATA.DEFAULT_SETTINGS }, blocks: [] };
    const settings = week.settings;
    const blocks = week.blocks;

    const showToast = (msg) => {
      setToast({ msg, t: Date.now() });
      clearTimeout(window.__toastTimer);
      window.__toastTimer = setTimeout(() => setToast(null), 1800);
    };

    const submitAuth = async (event) => {
      event.preventDefault();
      setAuthError("");
      setSyncStatus(authMode === "signup" ? "Creating account..." : "Signing in...");
      try {
        const payload = {
          email: authForm.email,
          password: authForm.password
        };
        if (authMode === "signup") {
          payload.name = authForm.name;
        }
        const result = await apiFetch(authMode === "signup" ? "/api/auth/signup" : "/api/auth/login", {
          method: "POST",
          body: JSON.stringify(payload)
        });
        setTeacher(result.teacher);
        await loadPlannerFromServer({ fallbackState: state, setState, setSyncStatus });
        setServerReady(true);
        setAuthForm({ name: "", email: "", password: "" });
        showToast(authMode === "signup" ? "Account ready" : "Welcome back");
      } catch (error) {
        setAuthError(error.message || "Could not sign in.");
        setSyncStatus("Sign in to sync");
      }
    };

    const logout = async () => {
      try {
        await apiFetch("/api/auth/logout", { method: "POST" });
      } catch (error) {}
      setTeacher(false);
      setServerReady(false);
      setEditorOpen(false);
      setSyncStatus("Signed out");
      showToast("Signed out");
    };

    const fireConfetti = () => {
      if (tweaks.motion < 2) return;
      const colors = PALETTE;
      const pieces = Array.from({ length: 30 + tweaks.motion * 3 }).map((_, i) => ({
        id: makeId() + i,
        x: 30 + Math.random() * 40,
        delay: Math.random() * 0.3,
        rotate: Math.random() * 360,
        color: colors[Math.floor(Math.random() * colors.length)],
        size: 10 + Math.random() * 10
      }));
      setConfetti(pieces);
      setTimeout(() => setConfetti([]), 2000);

      if (tweaks.sound) {
        try {
          const ctx = new (window.AudioContext || window.webkitAudioContext)();
          [880, 1320, 1760].forEach((f, i) => {
            const o = ctx.createOscillator(); const g = ctx.createGain();
            o.type = "triangle"; o.frequency.value = f;
            g.gain.value = 0; o.connect(g); g.connect(ctx.destination);
            g.gain.linearRampToValueAtTime(0.08, ctx.currentTime + 0.02 + i*0.06);
            g.gain.linearRampToValueAtTime(0, ctx.currentTime + 0.3 + i*0.06);
            o.start(ctx.currentTime + i*0.06); o.stop(ctx.currentTime + 0.35 + i*0.06);
          });
        } catch (e) {}
      }
    };

    /* Mutators */
    const setWeek = (mut) => {
      setState(s => {
        const w = s.weeks[s.currentWeekStart] || { settings: { ...window.PLANNER_DATA.DEFAULT_SETTINGS }, blocks: [] };
        const newW = typeof mut === "function" ? mut(w) : { ...w, ...mut };
        return { ...s, weeks: { ...s.weeks, [s.currentWeekStart]: newW } };
      });
    };

    const updateBlock = (id, patch) => {
      setWeek(w => ({ ...w, blocks: w.blocks.map(b => b.id === id ? normalizeBlock({ ...b, ...patch }) : b) }));
    };

    const createBlock = (overrides = {}) => {
      const baseStart = clamp(settings.startHour * 60 + 120, settings.startHour * 60, settings.endHour * 60 - 30);
      const b = normalizeBlock({
        id: makeId(),
        dayIndex: 0, start: baseStart, end: Math.min(baseStart + 45, settings.endHour * 60),
        title: "New lesson", typeId: "math",
        color: PALETTE[blocks.length % PALETTE.length],
        activities: [{ name: "Opening", minutes: 8, detail: "" }, { name: "Core activity", minutes: 25, detail: "" }, { name: "Wrap", minutes: 7, detail: "" }],
        ...overrides
      });
      setWeek(w => ({ ...w, blocks: [...w.blocks, b] }));
      setActiveId(b.id);
      setEditorOpen(true);
      showToast("New block added");
    };

    const placeSticker = (blockId, sticker) => {
      setWeek(w => ({
        ...w, blocks: w.blocks.map(b => b.id === blockId
          ? { ...b, stickers: [...(b.stickers||[]).filter(s => typeof s !== "string" || s !== sticker.kind), sticker] }
          : b)
      }));
      if (tweaks.motion >= 4) fireConfetti();
      showToast("✨ Sticker added");
    };

    const duplicateBlock = (id) => {
      const b = blocks.find(x => x.id === id);
      if (!b) return;
      const copy = normalizeBlock({ ...b, id: makeId(), start: Math.min(b.start + 15, 1425), end: Math.min(b.end + 15, 1440), title: b.title + " copy" });
      setWeek(w => ({ ...w, blocks: [...w.blocks, copy] }));
      setActiveId(copy.id);
      showToast("Duplicated");
    };

    const deleteBlock = (id) => {
      if (!confirm("Delete this lesson?")) return;
      setWeek(w => ({ ...w, blocks: w.blocks.filter(b => b.id !== id) }));
      setActiveId(null);
      setEditorOpen(false);
      showToast("Deleted");
    };

    const onSelect = (id, openEditor = false) => {
      setActiveId(id);
      if (openEditor) setEditorOpen(true);
    };

    /* Week nav */
    const shiftWeek = (days) => {
      setState(s => {
        const next = toISODate(addDays(parseISODate(s.currentWeekStart), days));
        const weeks = { ...s.weeks };
        if (!weeks[next]) weeks[next] = { settings: { ...window.PLANNER_DATA.DEFAULT_SETTINGS }, blocks: [] };
        return { ...s, currentWeekStart: next, weeks };
      });
      setActiveId(null);
      showToast(days < 0 ? "← Previous week" : "Next week →");
    };
    const jumpToday = () => {
      const ws = toISODate(startOfWeek(new Date()));
      setState(s => {
        const weeks = { ...s.weeks };
        if (!weeks[ws]) weeks[ws] = { settings: { ...window.PLANNER_DATA.DEFAULT_SETTINGS }, blocks: sampleBlocks() };
        return { ...s, currentWeekStart: ws, weeks };
      });
      showToast("Jumped to today");
    };

    const exportWeek = () => {
      const payload = { exportedAt: new Date().toISOString(), currentWeekStart, week };
      const blob = new Blob([JSON.stringify(payload, null, 2)], { type: "application/json" });
      const a = document.createElement("a");
      a.href = URL.createObjectURL(blob);
      a.download = "teaching-week-" + currentWeekStart + ".json";
      document.body.appendChild(a); a.click();
      URL.revokeObjectURL(a.href); a.remove();
      showToast("Exported");
      fireConfetti();
    };

    /* Stats */
    const totalMinutes = blocks.reduce((sum, b) => sum + Math.max(b.end - b.start, 0), 0);
    const planned = blocks.length ? Math.round(blocks.reduce((s,b) => s + planCompletion(b), 0) / blocks.length) : 0;

    const activeBlock = blocks.find(b => b.id === activeId) || null;

    /* Save command */
    const saveBlockAndCelebrate = () => {
      setEditorOpen(false);
      fireConfetti();
      showToast("Saved! 🎉");
    };

    /* Build week range string */
    const rangeStr = fmtDate(weekStartDate, { month: "short", day: "numeric" }) + " – " +
                     fmtDate(addDays(weekStartDate, settings.includeWeekend ? 6 : 4), { month: "short", day: "numeric", year: "numeric" });

    if (teacher === null) {
      return (
        <div className="auth-page">
          <window.PlannerScene motion={tweaks.motion} showWeather={tweaks.showWeather} theme={tweaks.theme}/>
          <div className="auth-card">
            <div className="brand auth-brand">
              <div className="brand-mark">
                <svg viewBox="0 0 40 40" fill="none" stroke="#2a2438" strokeWidth="3" strokeLinejoin="round" strokeLinecap="round">
                  <rect x="6" y="9" width="28" height="24" rx="4" fill="#fff"/>
                  <path d="M6 14h28"/>
                  <path d="M13 5v8M27 5v8"/>
                  <circle cx="14" cy="22" r="2" fill="#ff7aa9" stroke="none"/>
                  <circle cx="20" cy="22" r="2" fill="#5cd6b1" stroke="none"/>
                  <circle cx="26" cy="22" r="2" fill="#5ea7ff" stroke="none"/>
                </svg>
              </div>
              <div>
                <h1>My Teaching Week</h1>
                <div className="subline"><span className="pin"/>Loading your planner</div>
              </div>
            </div>
          </div>
        </div>
      );
    }

    if (!teacher) {
      return (
        <AuthScreen
          mode={authMode}
          form={authForm}
          error={authError}
          syncStatus={syncStatus}
          tweaks={tweaks}
          onMode={setAuthMode}
          onForm={setAuthForm}
          onSubmit={submitAuth}
        />
      );
    }

    return (
      <div className="app">
        {/* Background scene */}
        <window.PlannerScene motion={tweaks.motion} showWeather={tweaks.showWeather} theme={tweaks.theme}/>

        {/* Top bar */}
        <header className="topbar">
          <div className="brand">
            <div className="brand-mark">
              <svg viewBox="0 0 40 40" fill="none" stroke="#2a2438" strokeWidth="3" strokeLinejoin="round" strokeLinecap="round">
                <rect x="6" y="9" width="28" height="24" rx="4" fill="#fff"/>
                <path d="M6 14h28"/>
                <path d="M13 5v8M27 5v8"/>
                <circle cx="14" cy="22" r="2" fill="#ff7aa9" stroke="none"/>
                <circle cx="20" cy="22" r="2" fill="#5cd6b1" stroke="none"/>
                <circle cx="26" cy="22" r="2" fill="#5ea7ff" stroke="none"/>
                <circle cx="14" cy="28" r="2" fill="#ffc24c" stroke="none"/>
                <circle cx="20" cy="28" r="2" fill="#b07cff" stroke="none"/>
                <circle cx="26" cy="28" r="2" fill="#ff945c" stroke="none"/>
              </svg>
            </div>
            <div>
              <h1>My Teaching Week</h1>
              <div className="subline">
                <span className="pin"/>
                {rangeStr}
              </div>
            </div>
          </div>

          <div className="toolbar">
            <button className="tool-btn tb-prev icon-only" onClick={() => shiftWeek(-7)} title="Previous week">
              <window.Icon.left/>
            </button>
            <button className="tool-btn tb-today" onClick={jumpToday}>Today</button>
            <button className="tool-btn tb-next icon-only" onClick={() => shiftWeek(7)} title="Next week">
              <window.Icon.right/>
            </button>
            <button className="tool-btn" onClick={() => createBlock()} title="New block">
              <window.Icon.plus/> New
            </button>
            <button className="tool-btn tb-export" onClick={exportWeek} title="Export">
              <window.Icon.download/>
            </button>
            <button className="tool-btn tb-print icon-only" onClick={() => window.print()} title="Print">
              <window.Icon.print/>
            </button>
            <button className="tool-btn account-chip" onClick={logout} title="Sign out">
              {teacher.name || teacher.email} / Sign out
            </button>
          </div>
        </header>

        <div className="sync-line">
          {syncStatus}
        </div>

        {/* Metrics */}
        <div className="metrics">
          <div className="metric m-blocks">
            <div className="m-icon" style={{background:"var(--c-mon)"}}><window.Icon.book/></div>
            <div className="m-value">{blocks.length}</div>
            <div className="m-label">Lessons</div>
          </div>
          <div className="metric m-hours">
            <div className="m-icon" style={{background:"var(--c-thu)"}}><window.Icon.sun/></div>
            <div className="m-value">{fmtDur(totalMinutes)}</div>
            <div className="m-label">Teaching</div>
          </div>
          <div className="metric m-plan">
            <div className="m-icon" style={{background:"var(--c-wed)"}}><window.Icon.check/></div>
            <div className="m-value">{planned}%</div>
            <div className="m-label">Planned</div>
          </div>
        </div>

        {/* Board */}
        <window.PlannerBoard
          state={week}
          settings={settings}
          currentWeekStart={currentWeekStart}
          weekStartDate={weekStartDate}
          activeId={activeId}
          onSelect={onSelect}
          onCreate={(o) => createBlock(o)}
          onUpdate={updateBlock}
          onPlaceSticker={placeSticker}
          motion={tweaks.motion}
        />

        {/* Editor */}
        <window.PlannerEditor
          open={editorOpen && activeBlock}
          block={activeBlock}
          settings={settings}
          onClose={saveBlockAndCelebrate}
          onUpdate={updateBlock}
          onDuplicate={duplicateBlock}
          onDelete={deleteBlock}
        />

        {/* Toast */}
        <div className={"toast" + (toast ? " show" : "")}>
          {toast && toast.msg}
        </div>

        {/* Confetti */}
        {confetti.length > 0 && (
          <div className="confetti-layer">
            {confetti.map(p => (
              <div key={p.id} className="confetti-piece" style={{
                left: p.x + "%",
                top: "-30px",
                width: p.size + "px",
                height: (p.size * 1.4) + "px",
                background: p.color,
                animationDelay: p.delay + "s",
                transform: `rotate(${p.rotate}deg)`
              }}/>
            ))}
          </div>
        )}

        {/* Sticker tray */}
        <window.StickerTray visible={tweaks.showStickers && !editorOpen}/>

        {/* Floating "open editor" handle when a block is selected */}
        {activeBlock && !editorOpen && (
          <button
            onClick={() => setEditorOpen(true)}
            style={{
              position:"fixed", right: 22, bottom: 84, zIndex: 22,
              padding:"12px 18px", borderRadius:999,
              border:"3px solid var(--ink)", background:"var(--c-tue)",
              boxShadow:"6px 8px 0 var(--ink)", fontWeight:800
            }}
          >
            ✏️ Edit "{activeBlock.title.slice(0,18)}"
          </button>
        )}

        {/* Tweaks panel */}
        <window.PlannerTweaks/>
      </div>
    );
  }

  function AuthScreen({ mode, form, error, syncStatus, tweaks, onMode, onForm, onSubmit }) {
    const isSignup = mode === "signup";
    return (
      <div className="auth-page">
        <window.PlannerScene motion={tweaks.motion} showWeather={tweaks.showWeather} theme={tweaks.theme}/>
        <form className="auth-card" onSubmit={onSubmit}>
          <div className="brand auth-brand">
            <div className="brand-mark">
              <svg viewBox="0 0 40 40" fill="none" stroke="#2a2438" strokeWidth="3" strokeLinejoin="round" strokeLinecap="round">
                <rect x="6" y="9" width="28" height="24" rx="4" fill="#fff"/>
                <path d="M6 14h28"/>
                <path d="M13 5v8M27 5v8"/>
                <circle cx="14" cy="22" r="2" fill="#ff7aa9" stroke="none"/>
                <circle cx="20" cy="22" r="2" fill="#5cd6b1" stroke="none"/>
                <circle cx="26" cy="22" r="2" fill="#5ea7ff" stroke="none"/>
                <circle cx="14" cy="28" r="2" fill="#ffc24c" stroke="none"/>
                <circle cx="20" cy="28" r="2" fill="#b07cff" stroke="none"/>
              </svg>
            </div>
            <div>
              <h1>My Teaching Week</h1>
              <div className="subline"><span className="pin"/>Cloud saved weekly planners</div>
            </div>
          </div>

          <div className="auth-tabs">
            <button type="button" className={mode === "login" ? "active" : ""} onClick={() => onMode("login")}>Sign in</button>
            <button type="button" className={mode === "signup" ? "active" : ""} onClick={() => onMode("signup")}>Create account</button>
          </div>

          {isSignup && (
            <label className="auth-field">
              <span>Name</span>
              <input
                value={form.name}
                onChange={(event) => onForm({ ...form, name: event.target.value })}
                autoComplete="name"
                placeholder="Teacher name"
              />
            </label>
          )}

          <label className="auth-field">
            <span>Email</span>
            <input
              value={form.email}
              onChange={(event) => onForm({ ...form, email: event.target.value })}
              autoComplete="email"
              type="email"
              placeholder="teacher@example.com"
              required
            />
          </label>

          <label className="auth-field">
            <span>Password</span>
            <input
              value={form.password}
              onChange={(event) => onForm({ ...form, password: event.target.value })}
              autoComplete={isSignup ? "new-password" : "current-password"}
              type="password"
              minLength="8"
              placeholder="At least 8 characters"
              required
            />
          </label>

          {error && <div className="auth-error">{error}</div>}

          <button className="tool-btn auth-submit" type="submit">
            {isSignup ? "Create account" : "Sign in"}
          </button>

          <div className="auth-note">{syncStatus}</div>
        </form>
      </div>
    );
  }

  async function loadPlannerFromServer({ fallbackState, setState, setSyncStatus }) {
    const result = await apiFetch("/api/planner");
    const cloudWeeks = result.weeks || {};
    const hasCloudWeeks = Object.keys(cloudWeeks).length > 0;
    const nextState = normalizeState({
      currentWeekStart: fallbackState.currentWeekStart,
      weeks: hasCloudWeeks ? cloudWeeks : fallbackState.weeks
    });
    setState(nextState);
    setSyncStatus(hasCloudWeeks ? "Loaded from Cloudflare" : "Ready to save to Cloudflare");
  }

  async function apiFetch(url, options = {}) {
    const response = await fetch(url, {
      credentials: "include",
      headers: {
        "Content-Type": "application/json",
        ...(options.headers || {})
      },
      ...options
    });
    const payload = await response.json().catch(() => ({}));
    if (!response.ok) {
      throw new Error(payload.error || "Request failed");
    }
    return payload;
  }

  const root = ReactDOM.createRoot(document.getElementById("root"));
  root.render(<App/>);
})();
