import { loadChatRepository } from "@/data/repositories/chat_repository_loader";
import {
  resolveChatConversationKey,
  type ChatCacheIdentityResolver,
} from "@/data/repositories/chat_cache_identity";
import { Result, type Result as ResultT } from "@/utils/result";

import type { SplashLatestMessageCache } from "./splash_latest_message_cache";
import { getLatestSplashMessagePreview } from "./splash_latest_message_preview";

export interface LoadSplashLatestMessagePreviewInput {
  cache: SplashLatestMessageCache;
  characterId: string;
  fetchPreview?: (characterId: string) => Promise<ResultT<string | null>>;
  fetchPreviews?: () => Promise<
    ResultT<
      readonly {
        characterId: string;
        message: import("@/data/schemas/chat").ChatMessage | null;
      }[]
    >
  >;
  resolveIdentity?: ChatCacheIdentityResolver;
  resolvePreviewIdentity?: ChatConversationIdentityResolver;
}

type ChatConversationIdentityResolver = (
  characterId: string,
) => Promise<ResultT<string>>;

export async function resolveSplashLatestMessageCacheIdentity(
  characterId: string,
): Promise<string | null> {
  const result = await resolveChatConversationKey(characterId);
  return Result.isOk(result) ? result.data : null;
}

export async function loadSplashLatestMessagePreview({
  cache,
  characterId,
  fetchPreview = fetchSplashLatestMessagePreview,
  fetchPreviews = fetchSplashLatestMessagePreviews,
  resolveIdentity = () => resolveChatConversationKey(characterId),
  resolvePreviewIdentity = resolveChatConversationKey,
}: LoadSplashLatestMessagePreviewInput): Promise<ResultT<string | null>> {
  const identityResult = await resolveIdentity();
  const identity = Result.isOk(identityResult) ? identityResult.data : null;

  if (identity) {
    const cached = cache.get(identity);
    if (cached) return Result.ok(cached.message);
  }

  const result =
    fetchPreview !== fetchSplashLatestMessagePreview
      ? await fetchPreview(characterId)
      : await loadBatchPreview({
          cache,
          characterId,
          fetchPreviews,
          resolvePreviewIdentity,
        });
  if (identity && Result.isOk(result)) {
    cache.set(identity, result.data);
  }
  return result;
}

async function loadBatchPreview(input: {
  cache: SplashLatestMessageCache;
  characterId: string;
  fetchPreviews: NonNullable<LoadSplashLatestMessagePreviewInput["fetchPreviews"]>;
  resolvePreviewIdentity: ChatConversationIdentityResolver;
}): Promise<ResultT<string | null>> {
  const previews = await input.fetchPreviews();
  if (Result.isErr(previews)) {
    return fetchSplashLatestMessagePreview(input.characterId);
  }

  await Promise.all(
    previews.data.map(async (item) => {
      const identity = await input.resolvePreviewIdentity(item.characterId);
      if (Result.isErr(identity)) return;
      input.cache.set(
        identity.data,
        item.message ? getLatestSplashMessagePreview([item.message]) : null,
      );
    }),
  );
  const selected = previews.data.find(
    (item) => item.characterId === input.characterId,
  );
  return Result.ok(
    selected?.message
      ? getLatestSplashMessagePreview([selected.message])
      : null,
  );
}

export async function fetchSplashLatestMessagePreviews(): Promise<
  ResultT<
    readonly {
      characterId: string;
      message: import("@/data/schemas/chat").ChatMessage | null;
    }[]
  >
> {
  const repository = await loadChatRepository();
  const result = await repository.getPreviews();
  return Result.isErr(result) ? Result.err(result.error) : Result.ok(result.data.items);
}

export async function fetchSplashLatestMessagePreview(
  characterId: string,
): Promise<ResultT<string | null>> {
  const repository = await loadChatRepository();
  const result = await repository.getHistory(characterId, 1, 0);
  if (Result.isErr(result)) return Result.err(result.error);

  return Result.ok(getLatestSplashMessagePreview(result.data.messages));
}
