import {
  createContext,
  useContext,
  useEffect,
  useMemo,
  useState,
  type PropsWithChildren,
} from "react";
import { apiGet } from "./api";
import { AUTH_TOKEN_STORAGE_KEY, USER_STORAGE_KEY } from "./storage";
import type { SessionResponse, UserOption } from "./types";

interface SessionContextValue {
  user: UserOption | null;
  ready: boolean;
  setSession: (session: SessionResponse | null) => void;
}

const SessionContext = createContext<SessionContextValue>({
  user: null,
  ready: false,
  setSession: () => undefined,
});

function loadStoredUser(): UserOption | null {
  const raw = window.localStorage.getItem(USER_STORAGE_KEY);
  if (!raw) {
    return null;
  }
  try {
    return JSON.parse(raw) as UserOption;
  } catch {
    return null;
  }
}

export function SessionProvider({ children }: PropsWithChildren) {
  const [user, setUserState] = useState<UserOption | null>(() => loadStoredUser());
  const [ready, setReady] = useState(false);

  useEffect(() => {
    const token = window.localStorage.getItem(AUTH_TOKEN_STORAGE_KEY);
    if (!token) {
      setReady(true);
      return;
    }

    let cancelled = false;
    void apiGet<UserOption>("/session/me")
      .then((currentUser) => {
        if (!cancelled) {
          setUserState(currentUser);
        }
      })
      .catch(() => {
        if (!cancelled) {
          window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
          window.localStorage.removeItem(USER_STORAGE_KEY);
          setUserState(null);
        }
      })
      .finally(() => {
        if (!cancelled) {
          setReady(true);
        }
      });

    return () => {
      cancelled = true;
    };
  }, []);

  useEffect(() => {
    if (user) {
      window.localStorage.setItem(USER_STORAGE_KEY, JSON.stringify(user));
      return;
    }
    window.localStorage.removeItem(USER_STORAGE_KEY);
  }, [user]);

  function setSession(session: SessionResponse | null) {
    if (session) {
      window.localStorage.setItem(AUTH_TOKEN_STORAGE_KEY, session.token);
      setUserState(session.user);
      setReady(true);
      return;
    }
    window.localStorage.removeItem(AUTH_TOKEN_STORAGE_KEY);
    window.localStorage.removeItem(USER_STORAGE_KEY);
    setUserState(null);
    setReady(true);
  }

  const value = useMemo(
    () => ({
      user,
      ready,
      setSession,
    }),
    [ready, user],
  );

  return <SessionContext.Provider value={value}>{children}</SessionContext.Provider>;
}

export function useSession() {
  return useContext(SessionContext);
}
