/* global React, ReactDOM, Login, AppShell, LibraryScreen, PlayerScreen, MyLearningScreen, CleScreen, CollateralScreen, UpcomingScreen, SavedScreen, SpeakerScreen, SettingsScreen, Onboarding, Toast, VIDEOS, VIDEO_BY_ID, LEARNER, UPCOMING_LIVE, NOTIFICATIONS, useTweaks, TweaksPanel, TweakSection, TweakRadio, TweakToggle, TweakColor, AdminDashboardScreen, AdminCleScreen, AdminCoursesScreen, AdminLearnersScreen, WelcomeTour */
// LPE Learning Center — app shell, routing, state, tweaks.

const { useState: useStateApp, useCallback, useMemo: useMemoApp } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "hero": "ribbon",
  "libraryView": "grid",
  "accent": "#E47425",
  "showSidebarCta": true,
  "attendancePrompts": true,
  "liveDemo": false,
  "hideCle": true
}/*EDITMODE-END*/;

const ACCENT_700 = { "#E47425": "#C8631A", "#1B7F7C": "#156562", "#C9A876": "#A8884F" };

const db = {
  get(key, fallback) {
    try {
      const val = localStorage.getItem(key);
      return val ? JSON.parse(val) : fallback;
    } catch (e) {
      return fallback;
    }
  },
  set(key, val) {
    try {
      localStorage.setItem(key, JSON.stringify(val));
    } catch (e) {
      console.error(e);
    }
  }
};

function App() {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  window.APP_TWEAKS = t;
  
  // Auth Session Persistence
  const session = useMemoApp(() => db.get("lpe_auth_session", { authed: false, isAdmin: false, learner: LEARNER }), []);
  const [authed, setAuthed] = useStateApp(session.authed);
  const [isAdmin, setIsAdmin] = useStateApp(session.isAdmin);
  const [learner, setLearner] = useStateApp(session.learner);
  const [showTour, setShowTour] = useStateApp(false);
  
  const [view, setView] = useStateApp(session.authed ? (session.isAdmin ? "admin-dashboard" : "library") : "library");
  const [videoId, setVideoId] = useStateApp(null);
  const [liveMode, setLiveMode] = useStateApp(false);
  const [speakerName, setSpeakerName] = useStateApp(null);
  const [prevView, setPrevView] = useStateApp("library");
  const [query, setQuery] = useStateApp("");
  const [saved, setSaved] = useStateApp(() => new Set(db.get("lpe_saved_courses", ["LPE-W16"])));

  // Administrative / dynamic states
  const [videos, setVideos] = useStateApp(() => db.get("lpe_courses", window.VIDEOS));
  const videoMap = useMemoApp(() => Object.fromEntries(videos.map((v) => [v.id, v])), [videos]);

  const [cleRequests, setCleRequests] = useStateApp(() => db.get("lpe_cle_requests", [
    { id: "req-1", learnerName: "Nainika Sharma", learnerEmail: "nainika@sharma-acq.com", videoId: "LPE-W18", state: "Ohio", barNo: "OH-0094213", attendedOn: "2026-06-22", credits: 1.0, type: "General", status: "pending", timestamp: "2 hours ago" },
    { id: "req-2", learnerName: "John Doe", learnerEmail: "john@doe-law.com", videoId: "LPE-W14", state: "California", barNo: "CA-123456", attendedOn: "2026-06-19", credits: 1.0, type: "Ethics", status: "approved", timestamp: "1 day ago" },
    { id: "req-3", learnerName: "Sarah Jenkins", learnerEmail: "sarah@jenkinslegal.com", videoId: "LPE-W17", state: "Texas", barNo: "TX-789012", attendedOn: "2026-06-20", credits: 1.0, type: "General", status: "pending", timestamp: "3 hours ago" },
  ]));

  const [learners, setLearners] = useStateApp(() => db.get("lpe_learners", [
    { id: "l1", name: "Nainika Sharma", initials: "NS", role: "Buyer member", firm: "Sharma Acquisitions", email: "nainika@sharma-acq.com", barState: "Ohio", barNo: "OH-0094213", memberSince: "2024", onboarded: true, totalCredits: 2.0 },
    { id: "l2", name: "John Doe", initials: "JD", role: "Seller member", firm: "Doe & Partners", email: "john@doe-law.com", barState: "California", barNo: "CA-123456", memberSince: "2025", onboarded: true, totalCredits: 4.5 },
    { id: "l3", name: "Sarah Jenkins", initials: "SJ", role: "Buyer member", firm: "Jenkins Legal Group", email: "sarah@jenkinslegal.com", barState: "Texas", barNo: "TX-789012", memberSince: "2023", onboarded: false, totalCredits: 1.0 },
  ]));

  const [registered, setRegistered] = useStateApp(() => {
    const initial = UPCOMING_LIVE.filter((u) => u.registered).map((u) => u.id);
    return new Set(db.get("lpe_registered_live", initial));
  });

  const [upcoming, setUpcoming] = useStateApp(() => window.UPCOMING_LIVE || []);
  const [collateral, setCollateral] = useStateApp(() => window.COLLATERAL || []);
  
  const [progressMap, setProgressMap] = useStateApp(() => {
    const initial = {}; videos.forEach((v) => { if (v.progress > 0) initial[v.id] = v.progress; });
    return db.get("lpe_progress", initial);
  });
  
  const [attended, setAttended] = useStateApp(() => {
    const initial = videos.filter((v) => v.attended).map((v) => v.id);
    return new Set(db.get("lpe_attended", initial));
  });
  
  const [verifyMap, setVerifyMap] = useStateApp(() => db.get("lpe_verify_map", {}));
  const [notes, setNotes] = useStateApp(() => db.get("lpe_user_notes", {}));
  const [notifications, setNotifications] = useStateApp(() => db.get("lpe_notifications", NOTIFICATIONS));
  const [toast, setToast] = useStateApp(null);
  const [printPayload, setPrintPayload] = useStateApp(null);
  const [hydrated, setHydrated] = useStateApp(false);

  const pushHistory = useCallback((nextState) => {
    if (window.history && window.history.pushState) {
      window.history.pushState(nextState, "", "");
    }
  }, []);

  const navigate = useCallback((v) => {
    setView(v);
    setVideoId(null);
    setSpeakerName(null);
    window.scrollTo({ top: 0 });
    pushHistory({ view: v, videoId: null, speakerName: null });
  }, [pushHistory]);

  // Secure Server API fetch helper
  const apiFetch = useCallback(async (url, options = {}) => {
    const token = localStorage.getItem("lpe_session_token");
    const headers = {
      "Content-Type": "application/json",
      ...(options.headers || {})
    };
    if (token) {
      headers["Authorization"] = `Bearer ${token}`;
    }
    const res = await fetch(url, { ...options, headers });
    if (res.status === 401 && token) {
      localStorage.removeItem("lpe_session_token");
      localStorage.removeItem("lpe_auth_session");
      setAuthed(false);
      setIsAdmin(false);
      setLearner(LEARNER);
      navigate("library");
      window.location.reload();
    }
    return res;
  }, [navigate]);

  // Sync session and settings cache locally
  React.useEffect(() => {
    db.set("lpe_auth_session", { authed, isAdmin, learner });
  }, [authed, isAdmin, learner]);

  // Load and hydrate database states on mount
  React.useEffect(() => {
    const initializeApp = async () => {
      // 0. Auto-run DB init endpoint (handles zero-config setup)
      try {
        await fetch("/api/db-init");
      } catch (err) {
        console.error("DB init failed, continuing:", err);
      }

      // 1. Fetch live courses list from DB
      try {
        const coursesRes = await fetch("/api/courses");
        if (coursesRes.ok) {
          const coursesData = await coursesRes.json();
          if (coursesData.courses && coursesData.courses.length > 0) {
            const mergedCourses = coursesData.courses.map(c => {
              const localVideo = window.VIDEO_BY_ID && window.VIDEO_BY_ID[c.id];
              return {
                ...c,
                transcript: (c.transcript && c.transcript.length > 0) 
                  ? c.transcript 
                  : (localVideo && localVideo.transcript ? localVideo.transcript : [])
              };
            });
            setVideos(mergedCourses);
            db.set("lpe_courses", mergedCourses);
          } else {
            setVideos(window.VIDEOS || []);
          }
        } else {
          setVideos(window.VIDEOS || []);
        }
      } catch (err) {
        console.error("Failed to fetch courses:", err);
        setVideos(window.VIDEOS || []);
      }

      // 1.b Fetch upcoming live webinars from DB
      try {
        const eventsRes = await fetch("/api/live-events");
        if (eventsRes.ok) {
          const eventsData = await eventsRes.json();
          if (eventsData.events && eventsData.events.length > 0) {
            setUpcoming(eventsData.events);
          } else {
            setUpcoming(window.UPCOMING_LIVE || []);
          }
        } else {
          setUpcoming(window.UPCOMING_LIVE || []);
        }
      } catch (err) {
        console.error("Failed to fetch live events:", err);
        setUpcoming(window.UPCOMING_LIVE || []);
      }

      // 1.c Fetch collateral materials from DB
      try {
        const collateralRes = await fetch("/api/collateral");
        if (collateralRes.ok) {
          const collateralData = await collateralRes.json();
          if (collateralData.collateral && collateralData.collateral.length > 0) {
            setCollateral(collateralData.collateral);
          } else {
            setCollateral(window.COLLATERAL || []);
          }
        } else {
          setCollateral(window.COLLATERAL || []);
        }
      } catch (err) {
        console.error("Failed to fetch collateral:", err);
        setCollateral(window.COLLATERAL || []);
      }

      // 2. Fetch user session if token exists
      const token = localStorage.getItem("lpe_session_token");
      if (token) {
        try {
          const sessionRes = await apiFetch("/api/auth?action=session");
          if (sessionRes.ok) {
            const sessionData = await sessionRes.json();
            setAuthed(true);
            setIsAdmin(sessionData.user.isAdmin);
            setLearner(sessionData.user);

            // Populate user states
            if (sessionData.state) {
              setProgressMap(sessionData.state.progressMap || {});
              setAttended(new Set(sessionData.state.attended || []));
              setSaved(new Set(sessionData.state.saved || []));
              setRegistered(new Set(sessionData.state.registered || []));
              setNotes(sessionData.state.notes || {});
              setVerifyMap(sessionData.state.verifyMap || {});
              setNotifications(sessionData.state.notifications || []);
            }

            // Fetch admin panels if admin
            if (sessionData.user.isAdmin) {
              const reqsRes = await apiFetch("/api/cle-requests");
              if (reqsRes.ok) {
                const reqsData = await reqsRes.json();
                setCleRequests(reqsData.requests || []);
              }
              const learnersRes = await apiFetch("/api/learners");
              if (learnersRes.ok) {
                const learnersData = await learnersRes.json();
                setLearners(learnersData.learners || []);
              }
            }
          } else {
            // Bad token
            localStorage.removeItem("lpe_session_token");
            setAuthed(false);
          }
        } catch (err) {
          console.error("LMS Hydration Failed:", err);
        } finally {
          setHydrated(true);
        }
      } else {
        setHydrated(true);
      }
    };
    initializeApp();
  }, [apiFetch]);

  // Sync user progress & interactions dynamically to DB (debounced)
  React.useEffect(() => {
    if (!authed || isAdmin || !hydrated) return;
    const timer = setTimeout(() => {
      apiFetch("/api/user?action=sync", {
        method: "POST",
        body: JSON.stringify({
          progressMap,
          attended: Array.from(attended),
          saved: Array.from(saved),
          registered: Array.from(registered),
          notes,
          verifyMap,
          notifications
        })
      }).catch(console.error);
    }, 1000);
    return () => clearTimeout(timer);
  }, [progressMap, attended, saved, registered, notes, verifyMap, notifications, authed, isAdmin, apiFetch]);

  // Window Print Effect for PDF downloads
  React.useEffect(() => {
    if (printPayload) {
      const tid = setTimeout(() => {
        window.print();
        setPrintPayload(null);
      }, 150);
      return () => clearTimeout(tid);
    }
  }, [printPayload]);

  const showToast = useCallback((msg) => {
    setToast(msg);
    window.clearTimeout(showToast._t);
    showToast._t = window.setTimeout(() => setToast(null), 2600);
  }, []);

  const onSave = useCallback((id) => {
    setSaved((prev) => {
      const n = new Set(prev);
      if (n.has(id)) { n.delete(id); showToast("Removed from saved"); }
      else { n.add(id); showToast("Saved to your list"); }
      db.set("lpe_saved", Array.from(n));
      apiFetch("/api/user?action=sync", {
        method: "POST",
        body: JSON.stringify({ saved: Array.from(n) })
      }).catch(console.error);
      return n;
    });
  }, [showToast, apiFetch]);

  const toggleRegister = useCallback((id) => {
    setRegistered((prev) => {
      const n = new Set(prev);
      if (n.has(id)) { n.delete(id); showToast("Registration cancelled"); }
      else { n.add(id); showToast("Registered — we'll email a reminder"); }
      db.set("lpe_registered_live", Array.from(n));
      apiFetch("/api/user?action=sync", {
        method: "POST",
        body: JSON.stringify({ registered: Array.from(n) })
      }).catch(console.error);
      return n;
    });
  }, [showToast, apiFetch]);

  const setProgress = useCallback((id, pct) => {
    setProgressMap((m) => {
      const next = { ...m, [id]: Math.max(m[id] || 0, pct) };
      db.set("lpe_progress", next);
      apiFetch("/api/user?action=sync", {
        method: "POST",
        body: JSON.stringify({ progressMap: next })
      }).catch(console.error);
      return next;
    });
  }, [apiFetch]);

  const setVerify = useCallback((id, data) => {
    setVerifyMap((m) => {
      const next = { ...m, [id]: data };
      db.set("lpe_verify_map", next);
      apiFetch("/api/user?action=sync", {
        method: "POST",
        body: JSON.stringify({ verifyMap: next })
      }).catch(console.error);
      return next;
    });
  }, [apiFetch]);

  const onAttend = useCallback((id) => {
    const v = videoMap[id];
    if (!v) return;

    setCleRequests((prev) => {
      const prevArr = prev || [];
      if (prevArr.some(r => r.videoId === id && (r.status === 'pending' || r.status === 'approved'))) {
        return prev;
      }

      const newRequest = {
        id: "req-" + Date.now(),
        learnerName: learner ? learner.name : "Member",
        learnerEmail: learner ? learner.email : "",
        videoId: id,
        state: learner ? (learner.barState || "Ohio") : "Ohio",
        barNo: learner ? (learner.barNo || "Pending") : "Pending",
        attendedOn: new Date().toISOString().split('T')[0],
        credits: v.cle.credits,
        type: v.cle.type,
        status: "pending",
        timestamp: "Just now"
      };

      showToast("CLE affidavit submitted successfully for review");
      return [...prevArr, newRequest];
    });
  }, [showToast, videoMap, learner, setCleRequests]);

  // notes CRUD
  const addNote = useCallback((vid, note) => setNotes((m) => ({ ...m, [vid]: [...(m[vid] || []), note] })), []);
  const editNote = useCallback((vid, noteId, text) => setNotes((m) => ({ ...m, [vid]: (m[vid] || []).map((n) => n.id === noteId ? { ...n, text } : n) })), []);
  const deleteNote = useCallback((vid, noteId) => setNotes((m) => ({ ...m, [vid]: (m[vid] || []).filter((n) => n.id !== noteId) })), []);



  const openVideo = useCallback((id, opts = {}) => {
    if (!videoMap[id]) { showToast("That session is unavailable"); return; }
    setVideoId(id);
    setLiveMode(!!opts.live);
    setView((cur) => { if (cur !== "player" && cur !== "speaker") setPrevView(cur); return "player"; });
    window.scrollTo({ top: 0 });
    pushHistory({ view: "player", videoId: id, liveMode: !!opts.live, speakerName: null });
  }, [showToast, videoMap, pushHistory]);

  const openSpeaker = useCallback((name) => {
    setSpeakerName(name);
    setView((cur) => { if (cur !== "speaker" && cur !== "player") setPrevView(cur); return "speaker"; });
    window.scrollTo({ top: 0 });
    pushHistory({ view: "speaker", videoId: null, speakerName: name });
  }, [pushHistory]);

  // Synchronize React state with browser history (back/forward arrows)
  React.useEffect(() => {
    const handlePopState = (e) => {
      const state = e.state;
      if (state && state.view) {
        setView(state.view);
        setVideoId(state.videoId || null);
        setSpeakerName(state.speakerName || null);
        if (state.liveMode !== undefined) setLiveMode(state.liveMode);
      }
    };
    window.addEventListener("popstate", handlePopState);
    
    // Set initial history state
    window.history.replaceState({ view, videoId, speakerName, liveMode }, "", "");
    
    return () => window.removeEventListener("popstate", handlePopState);
  }, []);

  const markNotifRead = useCallback((id) => setNotifications((ns) => ns.map((n) => n.id === id ? { ...n, unread: false } : n)), []);
  const markAllRead = useCallback(() => setNotifications((ns) => ns.map((n) => ({ ...n, unread: false }))), []);
  const onNotif = useCallback((n) => {
    markNotifRead(n.id);
    if (n.videoId) openVideo(n.videoId); else navigate(n.view);
  }, [markNotifRead, openVideo, navigate]);

  const updateLearner = useCallback((patch) => {
    setLearner((l) => {
      const next = { ...l, ...patch };
      apiFetch("/api/user?action=update", {
        method: "POST",
        body: JSON.stringify(patch)
      }).catch(console.error);
      return next;
    });
  }, [apiFetch]);

  const completeOnboarding = useCallback((data) => {
    setLearner((l) => {
      const next = { ...l, onboarded: true, ...data };
      apiFetch("/api/user?action=update", {
        method: "POST",
        body: JSON.stringify({ onboarded: true, ...data })
      }).catch(console.error);
      return next;
    });
    showToast("Welcome to the Learning Center");
    navigate("library");
    setShowTour(true);
  }, [showToast, navigate, apiFetch]);

  React.useEffect(() => {
    console.log("App mounted / state changed:", { authed, hasLearner: !!learner, onboarded: learner?.onboarded });
    if (authed && learner && learner.onboarded) {
      const tourKey = "lpe_tour_seen_" + (learner.email || learner.id || "default");
      const tourSeen = localStorage.getItem(tourKey);
      console.log("Checking tour condition:", { tourKey, tourSeen });
      if (!tourSeen) {
        console.log("Triggering auto tour after mount delay");
        const tid = setTimeout(() => setShowTour(true), 400);
        return () => clearTimeout(tid);
      }
    }
  }, [authed, learner]);

  React.useEffect(() => {
    const handleStartTour = () => {
      console.log("Start tour event received");
      setShowTour(true);
    };
    window.addEventListener("start-tour", handleStartTour);
    return () => window.removeEventListener("start-tour", handleStartTour);
  }, []);

  // Derived sidebar stats
  const cleCredits = useMemoApp(() => [...attended].reduce((s, id) => s + (videoMap[id] && videoMap[id].cle && videoMap[id].cle.eligible ? videoMap[id].cle.credits : 0), 0), [attended, videoMap]);
  const inProgressCount = useMemoApp(() => videos.filter((v) => { const p = progressMap[v.id]; return p > 0 && p < 100; }).length, [progressMap, videos]);
  const unreadCount = notifications.filter((n) => n.unread).length;

  // Sign out handler
  const handleSignOut = useCallback(() => {
    localStorage.removeItem("lpe_session_token");
    localStorage.removeItem("lpe_auth_session");
    db.set("lpe_cle_requests", []);
    db.set("lpe_learners", []);
    setAuthed(false);
    setIsAdmin(false);
    setLearner(null);
    setProgressMap({});
    setAttended(new Set());
    setSaved(new Set());
    setRegistered(new Set());
    setNotes({});
    setVerifyMap({});
    setNotifications([]);
    navigate("library");
  }, [navigate]);

  const handleDeleteAccount = useCallback(async () => {
    try {
      const res = await apiFetch("/api/user?action=delete", { method: "DELETE" });
      if (res.ok) {
        showToast("Your account has been permanently deleted.");
        handleSignOut();
      } else {
        const data = await res.json();
        showToast(data.error || "Failed to delete account");
      }
    } catch (err) {
      console.error(err);
      showToast("Error deleting account");
    }
  }, [apiFetch, handleSignOut, showToast]);

  const handleDeleteLearner = useCallback(async (id) => {
    try {
      const res = await apiFetch(`/api/user?action=delete&userId=${id}`, { method: "DELETE" });
      if (res.ok) {
        showToast("Member account deleted successfully");
        setLearners((prev) => (prev || []).filter(l => l.id !== id));
      } else {
        const data = await res.json();
        showToast(data.error || "Failed to delete member");
      }
    } catch (err) {
      console.error(err);
      showToast("Error deleting member");
    }
  }, [apiFetch, showToast]);

  // Login/Signup API submission
  const handleLogin = useCallback(async (opts = {}) => {
    const endpoint = opts.fresh ? "/api/auth?action=signup" : "/api/auth?action=login";
    try {
      const response = await fetch(endpoint, {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({
          email: opts.email,
          password: opts.pw,
          name: opts.name,
          firm: opts.firm,
          role: opts.role,
          barState: opts.barState,
          barNo: opts.barNo
        })
      });
      const data = await response.json();
      if (!response.ok) {
        throw new Error(data.error || "Authentication failed");
      }
      
      if (data.pending) {
        showToast(data.message || "Application submitted. Pending approval.");
        return { success: true, pending: true, message: data.message };
      }
      
      localStorage.setItem("lpe_session_token", data.token);
      setAuthed(true);
      setIsAdmin(data.user.isAdmin);
      setLearner(data.user);
      
      if (data.state) {
        setProgressMap(data.state.progressMap || {});
        setAttended(new Set(data.state.attended || []));
        setSaved(new Set(data.state.saved || []));
        setRegistered(new Set(data.state.registered || []));
        setNotes(data.state.notes || {});
        setVerifyMap(data.state.verifyMap || {});
        setNotifications(data.state.notifications || []);
      }
      
      if (data.user.isAdmin) {
        const reqsRes = await apiFetch("/api/cle-requests");
        if (reqsRes.ok) {
          const reqsData = await reqsRes.json();
          setCleRequests(reqsData.requests || []);
        }
        const learnersRes = await apiFetch("/api/learners");
        if (learnersRes.ok) {
          const learnersData = await learnersRes.json();
          setLearners(learnersData.learners || []);
        }
        navigate("admin-dashboard");
      } else {
        navigate("library");
      }
      
      showToast(opts.fresh ? "Account created — Welcome to LPE!" : `Signed in as ${data.user.name}`);
      return { success: true };
    } catch (err) {
      showToast(err.message);
      return { success: false, error: err.message };
    }
  }, [apiFetch, showToast, navigate]);

  // Database administrative sync wrappers
  const setVideosWrapper = useCallback((update) => {
    setVideos((prev) => {
      const next = typeof update === "function" ? update(prev) : update;
      if (next.length > prev.length) {
        const added = next.find(n => !prev.some(p => p.id === n.id));
        if (added) {
          apiFetch("/api/courses", { method: "POST", body: JSON.stringify(added) }).catch(console.error);
        }
      } else if (next.length < prev.length) {
        const deleted = prev.find(p => !next.some(n => n.id === p.id));
        if (deleted) {
          apiFetch(`/api/courses?id=${deleted.id}`, { method: "DELETE" }).catch(console.error);
        }
      } else {
        const changed = next.find(n => {
          const p = prev.find(item => item.id === n.id);
          return p && JSON.stringify(p) !== JSON.stringify(n);
        });
        if (changed) {
          apiFetch("/api/courses", { method: "PUT", body: JSON.stringify(changed) }).catch(console.error);
        }
      }
      return next;
    });
  }, [apiFetch]);

  const setCleRequestsWrapper = useCallback((update) => {
    setCleRequests((prev) => {
      const next = typeof update === "function" ? update(prev) : update;
      if (next.length > prev.length) {
        const added = next.find(n => !prev.some(p => p.id === n.id));
        if (added) {
          apiFetch("/api/cle-requests", { method: "POST", body: JSON.stringify(added) }).catch(console.error);
        }
      } else if (next.length === prev.length) {
        const changed = next.find(n => {
          const p = prev.find(item => item.id === n.id);
          return p && p.status !== n.status;
        });
        if (changed) {
          apiFetch("/api/cle-requests", {
            method: "PATCH",
            body: JSON.stringify({ id: changed.id, status: changed.status })
          }).catch(console.error);
        }
      } else if (next.length < prev.length) {
        const removed = prev.find(p => !next.some(n => n.id === p.id));
        if (removed) {
          apiFetch(`/api/cle-requests?id=${removed.id}`, { method: "DELETE" }).catch(console.error);
        }
      }
      return next;
    });
  }, [apiFetch]);

  const setLearnersWrapper = useCallback((update) => {
    setLearners((prev) => {
      const next = typeof update === "function" ? update(prev) : update;
      const changed = next.find(n => {
        const p = prev.find(item => item.id === n.id);
        return p && p.onboarded !== n.onboarded;
      });
      if (changed) {
        apiFetch("/api/user?action=update", {
          method: "POST",
          body: JSON.stringify({ userId: changed.id, onboarded: changed.onboarded })
        }).catch(console.error);
      }
      return next;
    });
  }, [apiFetch]);

  const refreshAdminData = useCallback(async () => {
    if (!authed || !isAdmin) return;
    try {
      const reqsRes = await apiFetch("/api/cle-requests");
      if (reqsRes.ok) {
        const reqsData = await reqsRes.json();
        setCleRequests(reqsData.requests || []);
      }
      const learnersRes = await apiFetch("/api/learners");
      if (learnersRes.ok) {
        const learnersData = await learnersRes.json();
        setLearners(learnersData.learners || []);
      }
      showToast("Data refreshed from database");
    } catch (err) {
      console.error("Failed to refresh admin data:", err);
    }
  }, [authed, isAdmin, apiFetch, showToast]);

  if (!authed) {
    return <Login onLogin={handleLogin} />;
  }

  const accentStyle = { "--orange": t.accent, "--orange-700": ACCENT_700[t.accent] || "#C8631A" };

  const common = {
    tweaks: t,
    saved,
    onSave,
    onOpen: openVideo,
    onNavigate: navigate,
    openSpeaker,
    videos,
    videoMap,
    cleRequests,
    setCleRequests: setCleRequestsWrapper,
    learners,
    setLearners: setLearnersWrapper,
    onPrint: (payload) => setPrintPayload(payload),
    refreshAdminData,
    hideCle: t.hideCle,
    onToggleCle: () => setTweak("hideCle", !t.hideCle)
  };

  let screen;
  if (view === "admin-dashboard") screen = <AdminDashboardScreen {...common} showToast={showToast} />;
  else if (view === "admin-cle") screen = <AdminCleScreen {...common} showToast={showToast} setAttended={setAttended} />;
  else if (view === "admin-courses") screen = <AdminCoursesScreen {...common} setVideos={setVideosWrapper} showToast={showToast} />;
  else if (view === "admin-learners") screen = <AdminLearnersScreen {...common} showToast={showToast} onDeleteLearner={handleDeleteLearner} />;
  else if (view === "admin-events") screen = <AdminEventsScreen {...common} showToast={showToast} />;
  else if (view === "library") screen = <LibraryScreen query={query} progressMap={progressMap} {...common} />;
  else if (view === "player") screen = <PlayerScreen videoId={videoId} liveMode={liveMode || t.liveDemo} {...common} onBack={() => navigate(prevView)} progressMap={progressMap} setProgress={setProgress} attendedSet={attended} onAttend={onAttend} verifyMap={verifyMap} setVerify={setVerify} attendancePrompts={t.attendancePrompts} notes={notes} addNote={addNote} editNote={editNote} deleteNote={deleteNote} onToast={showToast} learner={learner} />;
  else if (view === "learning") screen = <MyLearningScreen progressMap={progressMap} attendedSet={attended} learner={learner} onToast={showToast} {...common} />;
  else if (view === "cle") screen = <CleScreen attendedSet={attended} verifyMap={verifyMap} learner={learner} updateLearner={updateLearner} onToast={showToast} onOpen={openVideo} {...common} />;
  else if (view === "collateral") screen = <CollateralScreen onToast={showToast} collateral={collateral} />;
  else if (view === "upcoming") screen = <UpcomingScreen registered={registered} toggleRegister={toggleRegister} onJoinLive={(id) => openVideo(id, { live: true })} onToast={showToast} upcoming={upcoming} {...common} />;
  else if (view === "saved") screen = <SavedScreen {...common} />;
  else if (view === "speaker") screen = <SpeakerScreen speakerName={speakerName} onBack={() => navigate(prevView)} {...common} progressMap={progressMap} />;
  else if (view === "settings") screen = <SettingsScreen learner={learner} updateLearner={updateLearner} tweaks={t} setTweak={setTweak} onToast={showToast} onSignOut={handleSignOut} onDeleteAccount={handleDeleteAccount} />;

  const activeNav = (view === "player" || view === "speaker" || view === "settings") ? prevView : view;
  const TourComponent = window.WelcomeTour;

  return (
    <div style={accentStyle}>
      {printPayload ? (
        <div className="printable-cert-wrap">
          {printPayload.type === "cert" ? (
            <PrintCertificate entry={printPayload.entry} learner={printPayload.learner} videoMap={videoMap} />
          ) : (
            <PrintAffidavit v={printPayload.video} form={printPayload.form} learner={printPayload.learner} />
          )}
        </div>
      ) : null}
      
      <div className={printPayload ? "no-print" : ""}>
        <AppShell
          active={activeNav}
          onNavigate={navigate}
          query={query}
          setQuery={(q) => { setQuery(q); if (view !== "library") navigate("library"); }}
          onAvatarNav={navigate}
          onSignOut={handleSignOut}
          onLogo={() => navigate(isAdmin ? "admin-dashboard" : "library")}
          learner={learner}
          notifications={notifications}
          unreadCount={unreadCount}
          onNotif={onNotif}
          markAllRead={markAllRead}
          upcomingCount={UPCOMING_LIVE.length}
          savedCount={saved.size}
          hideCle={t.hideCle}
          onToggleCle={() => setTweak("hideCle", !t.hideCle)}
          sidebarStats={{
            inProgress: inProgressCount,
            savedCount: saved.size,
            cleCredits,
            upcomingCount: UPCOMING_LIVE.length,
            showCta: t.showSidebarCta && !isAdmin && !t.hideCle,
            pendingCleCount: (cleRequests || []).filter((r) => r.status === "pending").length,
            hideCle: t.hideCle,
          }}
        >
          {screen}
        </AppShell>
        <Toast msg={toast} />

        {authed && learner && !learner.onboarded ? <Onboarding learner={learner} onComplete={completeOnboarding} /> : null}
        {showTour && TourComponent ? (
          <TourComponent
            isAdmin={isAdmin}
            onClose={() => {
              setShowTour(false);
              const tourKey = "lpe_tour_seen_" + (learner ? learner.email || learner.id || "default" : "default");
              localStorage.setItem(tourKey, "true");
            }}
          />
        ) : null}

        <TweaksPanel>
          <TweakSection label="Library landing" />
          <TweakRadio label="Featured treatment" value={t.hero}
            options={[{ value: "ribbon", label: "Ribbon" }, { value: "feature", label: "Hero" }, { value: "none", label: "None" }]}
            onChange={(v) => setTweak("hero", v)} />
          <TweakRadio label="Default view" value={t.libraryView}
            options={[{ value: "grid", label: "Grid" }, { value: "list", label: "List" }]}
            onChange={(v) => setTweak("libraryView", v)} />
          <TweakSection label="CLE & playback" />
          <TweakToggle label="Hide CLE features" value={t.hideCle} onChange={(v) => setTweak("hideCle", v)} />
          <TweakToggle label="Attendance prompts" value={t.attendancePrompts} onChange={(v) => setTweak("attendancePrompts", v)} />
          <TweakToggle label="Preview as live webinar" value={t.liveDemo} onChange={(v) => setTweak("liveDemo", v)} />
          <TweakSection label="Brand" />
          <TweakColor label="Accent" value={t.accent} options={["#E47425", "#1B7F7C", "#C9A876"]} onChange={(v) => setTweak("accent", v)} />
          <TweakToggle label="Sidebar CLE promo" value={t.showSidebarCta} onChange={(v) => setTweak("showSidebarCta", v)} />
        </TweaksPanel>
      </div>
    </div>
  );
}

// Printable Certificate Layout
function PrintCertificate({ entry, learner }) {
  const v = window.VIDEO_BY_ID[entry.videoId] || { title: "Course", sub: "", cle: { credits: 0, type: "General" }, code: entry.videoId, duration: 60 };
  
  // deterministic 4-char code
  let h = 0;
  const seed = v.id + entry.attendedOn;
  for (let i = 0; i < seed.length; i++) h = (h * 31 + seed.charCodeAt(i)) >>> 0;
  const chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
  let vcode = "";
  for (let i = 0; i < 4; i++) { vcode += chars[h % chars.length]; h = Math.floor(h / chars.length) + 7; }
  const yr = new Date(entry.attendedOn).getFullYear() || 2026;
  const codeNum = v.code.replace("LPE-", "");
  const fullVerifyCode = `LPE-${yr}-${codeNum}-${learner.initials}-${vcode}`;

  const isCle = v.cle && v.cle.eligible;
  const creditsVal = isCle ? v.cle.credits : (v.duration >= 60 ? v.duration / 60 : 1.0);
  const creditsLabel = isCle ? `${v.cle.type} credit hours` : "Instruction hours";

  return (
    <div className="cert">
      <div className="c-wm">The Law Practice Exchange</div>
      <div className="c-h">This certifies that</div>
      <div className="c-name">{learner.name}</div>
      <div className="c-h">has completed the {isCle ? "continuing legal education" : "professional development"} program</div>
      <div className="c-title">{v.title} — {v.sub}</div>
      <div className="c-grid">
        <div><div className="v">{creditsVal.toFixed(1)}</div><div className="l">{creditsLabel}</div></div>
        <div><div className="v">{entry.attendedOn}</div><div className="l">Date attended</div></div>
        <div><div className="v">{v.code}</div><div className="l">Program ID</div></div>
      </div>
      <div className="c-sign">
        <div>
          <div className="sig">Tom Lenfestey</div>
          <div className="sig-l">Founder, LPE · Program sponsor</div>
        </div>
        <div style={{ textAlign: "right" }}>
          <div className="sig" style={{ paddingRight: 0 }}>{entry.live ? "Live" : "On-demand"}</div>
          <div className="sig-l">Attendance type</div>
        </div>
      </div>
      <div style={{ marginTop: 18, paddingTop: 14, borderTop: "1px solid var(--grey-200)", display: "flex", justifyContent: "space-between", alignItems: "center", flexWrap: "wrap", gap: 8 }}>
        <span style={{ fontFamily: "var(--font-mono)", fontSize: 11, letterSpacing: "0.06em", color: "var(--navy)" }}>Verification: {fullVerifyCode}</span>
        <span className="small" style={{ fontSize: 11 }}>Verify at lawpracticeexchange.com/verify</span>
      </div>
    </div>
  );
}

// Printable Affidavit Layout
function PrintAffidavit({ v, form, learner }) {
  const todayLabel = () => new Date().toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric" });
  return (
    <div className="cert" style={{ borderColor: "var(--grey-300)" }}>
      <div className="c-wm" style={{ fontSize: 18 }}>The Law Practice Exchange</div>
      <div className="c-h" style={{ marginTop: 18 }}>Affidavit of attendance · {form.state} Bar</div>
      <p style={{ fontSize: 13, lineHeight: 1.7, color: "var(--navy)", marginTop: 14 }}>
        I, <b>{learner.name}</b> (Bar No. <b>{form.bar || "—"}</b>), affirm that I attended the continuing legal education program
        <b> “{v.title}”</b> ({v.code}) sponsored by The Law Practice Exchange on <b>{form.attended}</b>, comprising
        <b> {v.cle.credits.toFixed(1)} hour{v.cle.credits === 1 ? "" : "s"}</b> of {v.cle.type.toLowerCase()} instruction, and request that the credit be applied to my {form.state} reporting period.
      </p>
      <div className="c-sign" style={{ marginTop: 20 }}>
        <div><div className="sig">{learner.name}</div><div className="sig-l">Affiant signature</div></div>
        <div style={{ textAlign: "right" }}><div className="sig" style={{ paddingRight: 0 }}>{todayLabel()}</div><div className="sig-l">Date</div></div>
      </div>
    </div>
  );
}

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


