const { useState: mS, useEffect: mE, useMemo: mM, useCallback: mC } = React;

// ─── Router ───────────────────────────────────────────────────
function parseRoute(pathname) {
  const p = pathname.replace(/\/+$/, "") || "/";
  if (p === "/login") return { name: "login" };
  if (p === "/signup") return { name: "signup" };
  const m = p.match(/^\/u\/([^/]+)$/);
  if (m) return { name: "profile", username: decodeURIComponent(m[1]) };
  return { name: "index" };
}

function navigate(to) {
  window.history.pushState({}, "", to);
  window.dispatchEvent(new PopStateEvent("popstate"));
}

// ─── Photos shape helper ─────────────────────────────────────
function normalizeAlbums(raw) {
  return raw.map((a) => {
    // src  = 800px webp thumbnail (used by the grid — tiny, fast).
    // display = 2000px webp (used by the photo viewer and the album cover — crisp on retina, ~333KB avg).
    // full  = untouched original (download / fallback only).
    const photos = (a.photos || []).map((p) => ({
      ...p,
      src: p.thumbUrl || p.url,
      display: p.displayUrl || p.url,
      full: p.url,
    }));
    const coverPhoto = a.coverPhotoId ? photos.find((p) => p.id === a.coverPhotoId) : photos[0];
    return { ...a, photos, coverUrl: coverPhoto?.display || coverPhoto?.src || null };
  });
}

// ─── Top-level app ────────────────────────────────────────────
function PassageRoot() {
  const [route, setRoute] = mS(() => parseRoute(window.location.pathname));
  const [me, setMe] = mS(undefined); // undefined = loading, null = logged out, object = logged in

  mE(() => {
    const onPop = () => setRoute(parseRoute(window.location.pathname));
    window.addEventListener("popstate", onPop);
    return () => window.removeEventListener("popstate", onPop);
  }, []);

  mE(() => {
    API.me().then(setMe).catch(() => setMe(null));
  }, []);

  if (me === undefined) return <Splash />;

  // Index route: send logged-in users to their gallery, else to login.
  if (route.name === "index") {
    if (me) { navigate("/u/" + me.username); return <Splash />; }
    return <AuthPage mode="login" onDone={(u) => { setMe(u); navigate("/u/" + u.username); }} onSwitch={(m) => navigate("/" + m)} />;
  }
  if (route.name === "login")  return <AuthPage mode="login"  onDone={(u) => { setMe(u); navigate("/u/" + u.username); }} onSwitch={(m) => navigate("/" + m)} />;
  if (route.name === "signup") return <AuthPage mode="signup" onDone={(u) => { setMe(u); navigate("/u/" + u.username); }} onSwitch={(m) => navigate("/" + m)} />;

  if (route.name === "profile") {
    return (
      <ProfilePage
        username={route.username}
        me={me}
        setMe={setMe}
      />
    );
  }
  return <Splash />;
}

function Splash() {
  return (
    <div className="m-splash">
      <div className="m-splash-mark">◐</div>
    </div>
  );
}

// ─── Profile page ─────────────────────────────────────────────
function ProfilePage({ username, me, setMe }) {
  const [profileUser, setProfileUser] = mS(null);
  const [albums, setAlbums] = mS([]);
  const [loading, setLoading] = mS(true);
  const [notFound, setNotFound] = mS(false);
  const [view, setView] = mS({ kind: "list", tab: "albums" });
  const [viewer, setViewer] = mS(null);
  const [sheet, setSheet] = mS(null); // null | {kind:'new'|'edit', album?}
  const [profileSheet, setProfileSheet] = mS(false);
  const [busy, setBusy] = mS(false);
  const [uploading, setUploading] = mS(null); // null | { done, total }

  const canEdit = !!(me && profileUser && me.username === profileUser.username);

  async function refresh() {
    setLoading(true);
    try {
      const data = await API.profile(username);
      setProfileUser(data.user);
      setAlbums(normalizeAlbums(data.albums));
      setNotFound(false);
    } catch (e) {
      setNotFound(true);
    } finally { setLoading(false); }
  }
  mE(() => { refresh(); /* eslint-disable-next-line */ }, [username]);

  if (loading && !profileUser) return <Splash />;
  if (notFound) return (
    <div className="m-app">
      <MobileHeader view="list" title="Not found" right={null} leftOverride={<div className="m-hd-logo">◐</div>} />
      <div className="m-empty">
        <div className="m-empty-mark">◐</div>
        <div className="m-empty-t">No user @{username}</div>
        <div className="m-empty-s">This gallery doesn't exist.</div>
        <button className="m-btn-primary" onClick={() => navigate("/")}>Back</button>
      </div>
    </div>
  );

  const openAlbum = (id) => { setView({ kind: "album", id }); window.scrollTo(0, 0); };
  const back = () => { setView({ kind: "list", tab: "albums" }); window.scrollTo(0, 0); };
  const setTab = (t) => setView({ kind: "list", tab: t });

  const openPhoto = (albumId, idx) => setViewer({ albumId, idx });
  const closePhoto = () => setViewer(null);
  const viewerAlbum = viewer ? albums.find((a) => a.id === viewer.albumId) : null;
  const currentAlbum = view.kind === "album" ? albums.find((a) => a.id === view.id) : null;

  async function createAlbum(data) {
    setBusy(true);
    try {
      const a = await API.createAlbum(data);
      const fresh = normalizeAlbums([a]);
      setAlbums((prev) => [...fresh, ...prev]);
      setSheet(null);
      openAlbum(a.id);
    } finally { setBusy(false); }
  }
  async function editAlbum(data) {
    if (!currentAlbum) return;
    setBusy(true);
    try {
      const a = await API.updateAlbum(currentAlbum.id, data);
      setAlbums((prev) => prev.map((x) => x.id === a.id ? normalizeAlbums([{ ...a, photos: currentAlbum.photos.map((p) => ({ ...p, url: p.src })) }])[0] : x));
      setSheet(null);
    } finally { setBusy(false); }
  }
  async function addPhotos(albumId, files) {
    setUploading({ done: 0, total: files.length });
    const { errors } = await API.uploadPhotos(albumId, files, {
      onPhoto: (photo) => {
        const mapped = {
          ...photo,
          src: photo.thumbUrl || photo.url,
          display: photo.displayUrl || photo.url,
          full: photo.url,
        };
        setAlbums((prev) => prev.map((a) => {
          if (a.id !== albumId) return a;
          const photos = [...a.photos, mapped];
          const coverUrl = a.coverUrl || photos[0]?.display || photos[0]?.src || null;
          const coverPhotoId = a.coverPhotoId || photos[0]?.id || null;
          return { ...a, photos, coverUrl, coverPhotoId };
        }));
      },
      onProgress: ({ done, total }) => setUploading({ done, total }),
    });
    setUploading(null);
    if (errors.length) {
      const sample = errors.slice(0, 3).map((e) => `${e.file.name}: ${e.error}`).join("\n");
      const more = errors.length > 3 ? `\n…and ${errors.length - 3} more` : "";
      alert(`${errors.length} of ${files.length} failed. You can re-select just those and try again.\n\n${sample}${more}`);
    }
  }
  async function deletePhoto(albumId, idx) {
    const album = albums.find((a) => a.id === albumId);
    if (!album) return;
    const photo = album.photos[idx];
    if (!photo || !confirm("Delete this photo?")) return;
    await API.deletePhoto(photo.id);
    setAlbums((prev) => prev.map((a) => {
      if (a.id !== albumId) return a;
      const photos = a.photos.filter((p) => p.id !== photo.id);
      const wasCover = a.coverPhotoId === photo.id;
      const coverPhotoId = wasCover ? (photos[0]?.id || null) : a.coverPhotoId;
      const coverPhoto = coverPhotoId ? photos.find((p) => p.id === coverPhotoId) : null;
      const coverUrl = coverPhoto ? (coverPhoto.display || coverPhoto.src) : null;
      return { ...a, photos, coverPhotoId, coverUrl };
    }));
    setViewer((v) => {
      if (!v || v.albumId !== albumId) return v;
      const nextLen = album.photos.length - 1;
      if (nextLen <= 0) return null;
      return { ...v, idx: Math.min(v.idx, nextLen - 1) };
    });
  }
  async function setCover(albumId, idx) {
    const album = albums.find((a) => a.id === albumId);
    if (!album) return;
    const photo = album.photos[idx];
    if (!photo || album.coverPhotoId === photo.id) return;
    try { await API.setCover(albumId, photo.id); }
    catch (e) { alert("Couldn't set cover: " + (e.message || e)); return; }
    setAlbums((prev) => prev.map((a) =>
      a.id === albumId ? { ...a, coverPhotoId: photo.id, coverUrl: photo.display || photo.src } : a
    ));
  }
  async function deleteAlbum(id) {
    if (!confirm("Delete this album and all its photos?")) return;
    await API.deleteAlbum(id);
    setAlbums((prev) => prev.filter((a) => a.id !== id));
    back();
  }
  async function saveProfile(patch) {
    setBusy(true);
    try {
      const u = await API.updateMe(patch);
      setMe(u);
      setProfileUser(u);
      setProfileSheet(false);
    } finally { setBusy(false); }
  }
  async function logout() {
    await API.logout();
    setMe(null);
    navigate("/login");
  }

  const title = view.kind === "album" ? (currentAlbum?.title || "Album") : `@${profileUser.username}`;
  const right = (
    <div className="m-hd-right-group">
      {canEdit && view.kind === "list" && (
        <button className="m-hd-icon" aria-label="New album" onClick={() => setSheet({ kind: "new" })}>
          <svg width="22" height="22" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.8">
            <path d="M12 5v14M5 12h14" strokeLinecap="round" />
          </svg>
        </button>
      )}
      {!me && (
        <button className="m-hd-icon m-hd-text" onClick={() => navigate("/login")}>Log in</button>
      )}
      {me && (
        <button className="m-hd-icon" onClick={logout} aria-label="Log out" title={`Log out @${me.username}`}>
          <svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" strokeLinejoin="round">
            <path d="M15 17l5-5-5-5" />
            <path d="M20 12H9" />
            <path d="M12 4H6a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h6" />
          </svg>
        </button>
      )}
    </div>
  );

  return (
    <div className="m-app">
      <MobileHeader view={view.kind} onBack={back} title={title} right={right} />

      {view.kind === "list" && (
        <>
          <ProfileStrip
            profileUser={profileUser}
            albums={albums}
            canEdit={canEdit}
            onEdit={() => setProfileSheet(true)}
            onLogout={logout}
          />
          <TabStrip
            tab={view.tab}
            setTab={setTab}
            tabs={[
              { id: "albums", label: "Albums" },
              { id: "grid",   label: "All photos" },
            ]}
          />
          {view.tab === "albums" && <AlbumsList albums={albums} onOpen={openAlbum} onNew={() => setSheet({ kind: "new" })} canEdit={canEdit} />}
          {view.tab === "grid"   && <AllPhotosGrid albums={albums} onOpenPhoto={openPhoto} onNew={() => setSheet({ kind: "new" })} canEdit={canEdit} />}
        </>
      )}

      {view.kind === "album" && currentAlbum && (
        <AlbumView
          album={currentAlbum}
          canEdit={canEdit}
          onOpenPhoto={openPhoto}
          onAddPhotos={(files) => addPhotos(currentAlbum.id, files)}
          onDeleteAlbum={() => deleteAlbum(currentAlbum.id)}
          onEditAlbum={() => setSheet({ kind: "edit", album: currentAlbum })}
          uploading={uploading}
        />
      )}

      {viewer && viewerAlbum && (
        <PhotoViewer
          album={viewerAlbum}
          idx={viewer.idx}
          canEdit={canEdit}
          onClose={closePhoto}
          onChange={(i) => setViewer({ ...viewer, idx: i })}
          onDelete={(i) => deletePhoto(viewer.albumId, i)}
          onSetCover={(i) => setCover(viewer.albumId, i)}
        />
      )}

      {canEdit && view.kind === "list" && albums.length > 0 && (
        <Fab onClick={() => setSheet({ kind: "new" })} label="New album" />
      )}

      {sheet && (
        <AlbumSheet
          open={true}
          initial={sheet.kind === "edit" ? sheet.album : null}
          onClose={() => setSheet(null)}
          onSave={sheet.kind === "edit" ? editAlbum : createAlbum}
          busy={busy}
        />
      )}

      {profileSheet && (
        <ProfileSheet
          open={true}
          initial={profileUser}
          onClose={() => setProfileSheet(false)}
          onSave={saveProfile}
          busy={busy}
        />
      )}
    </div>
  );
}

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