"use client";

import {
  createContext,
  useCallback,
  useEffect,
  type ReactNode,
  useContext,
  useState,
} from "react";

import {
  CHARACTERS,
  createCharacterCatalog,
  DEFAULT_CHARACTER,
  mergeRemoteCharacterCatalog,
  type CharacterCatalog,
  type CharacterProfile,
} from "@/data/constants/character";
import { getCharacterRepository } from "@/data/repositories/character_repository";
import { AppEnvUtil } from "@/utils/app-env";
import { Result } from "@/utils/result";

export type CharacterCatalogStatus = "loading" | "ready" | "refreshing";

export interface CharacterCatalogContextValue extends CharacterCatalog {
  readonly status: CharacterCatalogStatus;
  readonly defaultCharacter: CharacterProfile;
  readonly isFallback: boolean;
  refresh(): Promise<void>;
}

const CharacterCatalogContext =
  createContext<CharacterCatalogContextValue | null>(null);

export interface CharacterCatalogProviderProps {
  children: ReactNode;
  characters?: readonly CharacterProfile[];
}

export function CharacterCatalogProvider({
  children,
  characters = CHARACTERS,
}: CharacterCatalogProviderProps) {
  const fallbackCharacters = AppEnvUtil.isProduction()
    ? characters.filter((character) => character.id === DEFAULT_CHARACTER.id)
    : characters;
  const [state, setState] = useState(() => ({
    catalog: createCharacterCatalog(fallbackCharacters),
    defaultCharacter: DEFAULT_CHARACTER,
    status: "loading" as CharacterCatalogStatus,
    isFallback: true,
    hasRemoteSnapshot: false,
  }));

  const refresh = useCallback(async () => {
    setState((current) => ({
      ...current,
      status: current.hasRemoteSnapshot ? "refreshing" : "loading",
    }));
    const result = await getCharacterRepository().getChatCharacters();
    if (Result.isErr(result)) {
      setState((current) => ({ ...current, status: "ready" }));
      return;
    }
    const snapshot = mergeRemoteCharacterCatalog(result.data, characters);
    setState({
      ...snapshot,
      status: "ready",
      isFallback: false,
      hasRemoteSnapshot: true,
    });
  }, [characters]);

  useEffect(() => {
    let cancelled = false;
    void getCharacterRepository()
      .getChatCharacters()
      .then((result) => {
        if (cancelled) return;
        if (Result.isErr(result)) {
          setState((current) => ({ ...current, status: "ready" }));
          return;
        }
        const snapshot = mergeRemoteCharacterCatalog(result.data, characters);
        setState({
          ...snapshot,
          status: "ready",
          isFallback: false,
          hasRemoteSnapshot: true,
        });
      });
    return () => {
      cancelled = true;
    };
  }, [characters]);

  const value: CharacterCatalogContextValue = {
    ...state.catalog,
    status: state.status,
    defaultCharacter: state.defaultCharacter,
    isFallback: state.isFallback,
    refresh,
  };
  return (
    <CharacterCatalogContext.Provider value={value}>
      {children}
    </CharacterCatalogContext.Provider>
  );
}

export function useCharacterCatalog(): CharacterCatalogContextValue {
  const catalog = useContext(CharacterCatalogContext);
  if (!catalog) {
    throw new Error(
      "useCharacterCatalog must be used within CharacterCatalogProvider.",
    );
  }
  return catalog;
}
