import { useCallback, useEffect, useState } from "react";
import { createRoot } from "react-dom/client";
import CmsApp, { NavigationProgress } from "./components/CmsApp";
import "./styles/global.css";
import "./styles/design-system.css";

type Session = {
  email: string;
  isAdmin: boolean;
};

function isSession(value: unknown): value is Session {
  return Boolean(
    value
      && typeof value === "object"
      && "email" in value
      && typeof value.email === "string"
      && value.email.length > 0
      && "isAdmin" in value
      && typeof value.isAdmin === "boolean"
  );
}

async function loadSession(signal: AbortSignal): Promise<Session> {
  const response = await fetch("/api/session", {
    headers: { Accept: "application/json" },
    cache: "no-store",
    credentials: "same-origin",
    redirect: "error",
    signal
  });

  if (!response.ok) {
    throw new Error(
      response.status === 401
        ? "Your session has expired. Refresh and sign in again."
        : "Your account could not be loaded."
    );
  }

  const contentType = response.headers.get("content-type") ?? "";
  if (!contentType.includes("application/json")) {
    throw new Error("Your account could not be loaded.");
  }

  const session: unknown = await response.json();
  if (!isSession(session)) throw new Error("Your account could not be loaded.");
  return session;
}

function Bootstrap() {
  const [session, setSession] = useState<Session | null>(null);
  const [error, setError] = useState("");
  const [attempt, setAttempt] = useState(0);

  const retry = useCallback(() => {
    setError("");
    setAttempt((value) => value + 1);
  }, []);

  useEffect(() => {
    const controller = new AbortController();
    void loadSession(controller.signal)
      .then(setSession)
      .catch((reason: unknown) => {
        if (!controller.signal.aborted) {
          setError(reason instanceof Error ? reason.message : "Your account could not be loaded.");
        }
      });
    return () => controller.abort();
  }, [attempt]);

  if (session) return <CmsApp email={session.email} />;

  return (
    <main className="shell" aria-busy={!error}>
      <NavigationProgress active={!error} label="Loading Ente Content" />
      <header className="topbar">
        <div className="brand">
          <img src="/ente-branding.svg" alt="Ente" />
          <span className="product">Content</span>
        </div>
      </header>
      <section className={`app-state app-state-${error ? "error" : "loading"}`}>
        <div className="app-state-content">
          <span className="app-state-indicator" aria-hidden="true" />
          {error ? (
            <>
              <h1>Ente Content could not open</h1>
              <p role="alert">{error}</p>
              <button className="primary" type="button" onClick={retry}>Try again</button>
            </>
          ) : (
            <>
              <h1>Opening Ente Content</h1>
              <p role="status">Loading your publishing workspace</p>
            </>
          )}
        </div>
      </section>
    </main>
  );
}

const root = document.getElementById("root");
if (!root) throw new Error("Application root was not found.");
createRoot(root).render(<Bootstrap />);
