// app.jsx — Everest mission control entry

const { useState: useStateA, useEffect: useEffectA } = React;

const TWEAK_DEFAULTS = /*EDITMODE-BEGIN*/{
  "fontPairing": "editorial",
  "accent": "#C9512B",
  "density": "regular",
  "showTopo": true
}/*EDITMODE-END*/;

// Font pairing presets
const FONT_PAIRINGS = {
  editorial: {
    label: "Editorial",
    display: '"Instrument Serif", Georgia, serif',
    sans:    '"Geist", ui-sans-serif, system-ui, sans-serif',
    mono:    '"JetBrains Mono", ui-monospace, monospace',
    google:  "Instrument+Serif:ital@0;1|Geist:wght@400;500;600;700|JetBrains+Mono:wght@400;500",
  },
  newsroom: {
    label: "Newsroom",
    display: '"Newsreader", Georgia, serif',
    sans:    '"Manrope", ui-sans-serif, system-ui, sans-serif',
    mono:    '"JetBrains Mono", ui-monospace, monospace',
    google:  "Newsreader:ital,opsz,wght@0,6..72,400;1,6..72,400|Manrope:wght@400;500;600;700|JetBrains+Mono:wght@400;500",
  },
  classical: {
    label: "Classical",
    display: '"EB Garamond", Georgia, serif',
    sans:    '"Space Grotesk", ui-sans-serif, system-ui, sans-serif',
    mono:    '"JetBrains Mono", ui-monospace, monospace',
    google:  "EB+Garamond:ital,wght@0,400;0,500;1,400|Space+Grotesk:wght@400;500;600;700|JetBrains+Mono:wght@400;500",
  },
  alpine: {
    label: "Alpine",
    display: '"Cormorant Garamond", Georgia, serif',
    sans:    '"Inter Tight", ui-sans-serif, system-ui, sans-serif',
    mono:    '"IBM Plex Mono", ui-monospace, monospace',
    google:  "Cormorant+Garamond:ital,wght@0,400;0,500;1,400|Inter+Tight:wght@400;500;600;700|IBM+Plex+Mono:wght@400;500",
  },
};

function injectGoogleFont(spec) {
  const id = "gf-everest";
  const old = document.getElementById(id);
  if (old) old.remove();
  const link = document.createElement("link");
  link.id = id;
  link.rel = "stylesheet";
  const families = spec.split("|").map(f => "family=" + f).join("&");
  link.href = "https://fonts.googleapis.com/css2?" + families + "&display=swap";
  document.head.appendChild(link);
}

function App({ onLogout, currentUser }) {
  const [t, setTweak] = useTweaks(TWEAK_DEFAULTS);
  const [view, setView] = useStateA("dashboard"); // dashboard | project | notes | receipts | symphony
  const [activeProjectId, setActiveProjectId] = useStateA(null);
  const [projects, setProjects] = useStateA([]);
  const [notes, setNotes] = useStateA([]);
  const [receipts, setReceipts] = useStateA([]);
  const [campFiles, setCampFiles] = useStateA([]);

  // ─── Load data from API on mount ──────────────────────────────────
  useEffectA(() => {
    fetch("/api/v1/projects")
      .then(r => r.json())
      .then(data => setProjects(data))
      .catch(err => console.error("Failed to load projects:", err));

    fetch("/api/v1/notes")
      .then(r => r.json())
      .then(data => setNotes(data))
      .catch(err => console.error("Failed to load notes:", err));

    fetch("/api/v1/receipts")
      .then(r => r.json())
      .then(data => setReceipts(data))
      .catch(err => console.error("Failed to load receipts:", err));

    fetch("/api/v1/camp-files")
      .then(r => r.json())
      .then(data => setCampFiles(data))
      .catch(err => console.error("Failed to load camp files:", err));
  }, []);

  // ─── Apply font pairing ────────────────────────────────────────────
  useEffectA(() => {
    const p = FONT_PAIRINGS[t.fontPairing] || FONT_PAIRINGS.editorial;
    injectGoogleFont(p.google);
    document.documentElement.style.setProperty("--font-display", p.display);
    document.documentElement.style.setProperty("--font-sans", p.sans);
    document.documentElement.style.setProperty("--font-mono", p.mono);
  }, [t.fontPairing]);

  // ─── Apply accent ──────────────────────────────────────────────────
  useEffectA(() => {
    document.documentElement.style.setProperty("--accent", t.accent);
  }, [t.accent]);

  // ─── Apply density ─────────────────────────────────────────────────
  useEffectA(() => {
    document.documentElement.setAttribute("data-density", t.density);
  }, [t.density]);

  // ─── Actions ──────────────────────────────────────────────────────
  const openProject = (id) => {
    setActiveProjectId(id);
    setView("project");
    window.scrollTo({ top: 0, behavior: "instant" });
  };

  const backToBase = () => {
    setView("dashboard");
    window.scrollTo({ top: 0, behavior: "instant" });
  };

  const switchProject = (id) => {
    setActiveProjectId(id);
    window.scrollTo({ top: 0, behavior: "instant" });
  };

  const markWorked = async (id) => {
    const today = new Date().toISOString().slice(0, 10);
    // Optimistic update
    setProjects(ps => ps.map(p => p.id === id
      ? { ...p, last_worked: "Worked today", last_worked_at: today }
      : p));
    try {
      const r = await fetch(`/api/v1/projects/${id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ last_worked: "Worked today", last_worked_at: today }),
      });
      if (!r.ok) throw new Error("PATCH failed");
      const updated = await r.json();
      setProjects(ps => ps.map(p => p.id === id ? updated : p));
    } catch (err) {
      console.error("Failed to mark worked:", err);
    }
  };

  const addNote = async (text, projectId = null) => {
    try {
      const r = await fetch("/api/v1/notes", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ text, project_id: projectId }),
      });
      if (!r.ok) throw new Error("POST /api/v1/notes failed");
      const note = await r.json();
      setNotes(ns => [note, ...ns]);
    } catch (err) {
      console.error("Failed to add note:", err);
    }
  };

  const patchProject = async (id, updates) => {
    setProjects(ps => ps.map(p => p.id === id ? { ...p, ...updates } : p));
    try {
      const r = await fetch(`/api/v1/projects/${id}`, {
        method: "PATCH",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(updates),
      });
      if (!r.ok) throw new Error("PATCH failed");
      const updated = await r.json();
      setProjects(ps => ps.map(p => p.id === id ? updated : p));
    } catch (err) {
      console.error("Failed to patch project:", err);
    }
  };

  const deleteNote = async (id) => {
    setNotes(ns => ns.filter(n => n.id !== id));
    try {
      await fetch(`/api/v1/notes/${id}`, { method: "DELETE" });
    } catch (err) {
      console.error("Failed to delete note:", err);
    }
  };

  const uploadReceipt = async (file, projectId) => {
    const fd = new FormData();
    fd.append("file", file);
    if (projectId) fd.append("project_id", projectId);
    try {
      const r = await fetch("/api/v1/receipts/upload", { method: "POST", body: fd });
      if (!r.ok) throw new Error("POST /api/v1/receipts/upload failed");
      const receipt = await r.json();
      setReceipts(rs => [...rs, receipt]);
    } catch (err) {
      console.error("Failed to upload receipt:", err);
    }
  };

  const uploadCampFile = async (file, projectId) => {
    const fd = new FormData();
    fd.append("file", file);
    try {
      const r = await fetch(`/api/v1/projects/${projectId}/files`, { method: "POST", body: fd });
      if (!r.ok) throw new Error("POST /api/v1/projects/{id}/files failed");
      const cf = await r.json();
      setCampFiles(cfs => [...cfs, cf]);
    } catch (err) {
      console.error("Failed to upload camp file:", err);
    }
  };

  const deleteCampFile = async (fileId) => {
    try {
      const r = await fetch(`/api/v1/camp-files/${fileId}`, { method: "DELETE" });
      if (!r.ok) throw new Error("DELETE /api/v1/camp-files/{id} failed");
      setCampFiles(cfs => cfs.filter(f => f.id !== fileId));
    } catch (err) {
      console.error("Failed to delete camp file:", err);
    }
  };

  const createProject = async ({ name, description, folder }) => {
    const initials = name.split(" ").map(w => w[0] || "").join("").toUpperCase().slice(0, 2) || "XX";
    const seq = String(projects.length + 1).padStart(2, "0");
    const elevList = ["8,902","9,843","11,247","12,500","13,114","14,505","15,260","16,730"];
    const elevation = elevList[Math.floor(Math.random() * elevList.length)];
    const body = {
      name,
      description,
      folder: folder || `~/work/${name.toLowerCase().replace(/\s+/g, "-")}/`,
      codename: `${initials}–${seq} · ${name.toUpperCase()}`,
      elevation,
      coords: "",
      status: "Ascending",
      progress: 0.0,
    };
    try {
      const r = await fetch("/api/v1/projects", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify(body),
      });
      if (!r.ok) throw new Error("POST /api/v1/projects failed");
      const project = await r.json();
      setProjects(ps => [...ps, project]);
    } catch (err) {
      console.error("Failed to create project:", err);
    }
  };

  const activeProject = projects.find(p => p.id === activeProjectId);

  // ─── Adapt API snake_case fields for project.jsx which uses camelCase ──
  // project.jsx reads: project.lastWorked, project.endDate, project.startDate
  // API returns:       last_worked, end_date, start_date
  // We normalise by injecting camelCase aliases when passing down.
  const normaliseProject = (p) => ({
    ...p,
    lastWorked: p.last_worked,
    lastWorkedAt: p.last_worked_at,
    endDate: p.end_date,
    startDate: p.start_date,
    docs: p.docs || [],
    team: p.team || [],
    notes: p.notes || [],
    activity: p.activity || [],
    images: p.images || [],
  });

  const normalisedProjects = projects.map(normaliseProject);
  const normalisedActive = activeProject ? normaliseProject(activeProject) : null;

  return (
    <>
      <TopoBackdrop />
      <div className="app">
        <Topbar onHome={backToBase} view={view} onNav={(v) => { if(v==="dashboard") backToBase(); else setView(v); }} onLogout={onLogout} currentUser={currentUser} />

        {view === "dashboard" && (
          <Dashboard
            projects={normalisedProjects}
            notes={notes.filter(n => !n.project_id)}
            receipts={receipts}
            onOpen={openProject}
            onMarkWorked={markWorked}
            onAddNote={(text) => addNote(text, null)}
            onUploadReceipt={uploadReceipt}
            onCreateProject={createProject}
            onDeleteNote={deleteNote}
          />
        )}

        {view === "project" && normalisedActive && (
          <ProjectDetail
            project={normalisedActive}
            allProjects={normalisedProjects}
            notes={notes}
            receipts={receipts}
            campFiles={campFiles}
            onBack={backToBase}
            onSwitch={switchProject}
            onMarkWorked={markWorked}
            onAddNote={(text, projectId) => addNote(text, projectId)}
            onSave={(updates) => patchProject(activeProjectId, updates)}
            onUploadReceipt={uploadReceipt}
            onUploadCampFile={uploadCampFile}
            onDeleteCampFile={deleteCampFile}
            onDeleteNote={deleteNote}
            currentUser={currentUser}
          />
        )}

        {view === "notes" && (
          <NotesView
            notes={notes}
            onAdd={(text, projectId) => addNote(text, projectId)}
            onDelete={async (id) => {
              try {
                await fetch(`/api/v1/notes/${id}`, { method: "DELETE" });
                setNotes(ns => ns.filter(n => n.id !== id));
              } catch(e) { console.error("Delete note failed", e); }
            }}
          />
        )}

        {view === "receipts" && (
          <ReceiptsView
            receipts={receipts}
            projects={normalisedProjects}
            onUpload={uploadReceipt}
          />
        )}

        {view === "symphony" && <SymphonyView />}
      </div>

      <TweaksPanel title="Tweaks">
        <TweakSection label="Typography" />
        <TweakSelect
          label="Font pairing"
          value={t.fontPairing}
          options={Object.entries(FONT_PAIRINGS).map(([k, v]) => ({ value: k, label: v.label }))}
          onChange={(v) => setTweak("fontPairing", v)}
        />
        <TweakSection label="Style" />
        <TweakColor
          label="Accent"
          value={t.accent}
          options={["#C9512B", "#3B5240", "#4A6E85", "#7A5AE0", "#1A1F2B"]}
          onChange={(v) => setTweak("accent", v)}
        />
        <TweakRadio
          label="Density"
          value={t.density}
          options={["compact", "regular", "comfy"]}
          onChange={(v) => setTweak("density", v)}
        />
      </TweaksPanel>
    </>
  );
}

function Root() {
  const [auth, setAuth] = useStateA(null); // null=checking, false=login, true=app
  const [authView, setAuthView] = useStateA("login"); // "login" | "register"
  const [currentUser, setCurrentUser] = useStateA(null);

  async function fetchCurrentUser() {
    try {
      const res = await fetch("/api/v1/auth/me");
      if (res.ok) setCurrentUser(await res.json());
    } catch (_) {}
  }

  useEffectA(() => {
    fetch("/api/v1/auth/me")
      .then(r => {
        if (r.ok) {
          r.json().then(u => setCurrentUser(u));
          setAuth(true);
        } else {
          setAuth(false);
        }
      })
      .catch(() => setAuth(false));
  }, []);

  const handleLogin = () => {
    fetchCurrentUser();
    setAuth(true);
  };

  const handleLogout = async () => {
    await fetch("/api/v1/auth/logout", { method: "POST" });
    setCurrentUser(null);
    setAuth(false);
    setAuthView("login");
  };

  if (auth === null) return null;
  if (!auth) {
    if (authView === "register") {
      return <RegisterForm onLogin={handleLogin} onBack={() => setAuthView("login")} />;
    }
    return <LoginScreen onLogin={handleLogin} onRegister={() => setAuthView("register")} />;
  }
  return (
    <>
      <App onLogout={handleLogout} currentUser={currentUser} />
    </>
  );
}

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




