import { create } from "zustand";
import { combine } from "zustand/middleware";
import { memoServiceClient } from "@/grpcweb";
import { CreateMemoRequest, ListMemosRequest, Memo } from "@/types/proto/api/v2/memo_service";

interface State {
  memoMapById: Record<number, Memo>;
}

const getDefaultState = (): State => ({
  memoMapById: {},
});

export const useMemoStore = create(
  combine(getDefaultState(), (set, get) => ({
    setState: (state: State) => set(state),
    getState: () => get(),
    fetchMemos: async (request: Partial<ListMemosRequest>) => {
      const { memos, nextPageToken } = await memoServiceClient.listMemos(request);
      const memoMap = get().memoMapById;
      for (const memo of memos) {
        memoMap[memo.id] = memo;
      }
      set({ memoMapById: memoMap });
      return { memos, nextPageToken };
    },
    getOrFetchMemoById: async (id: number, options?: { skipCache?: boolean; skipStore?: boolean }) => {
      const memoMap = get().memoMapById;
      const memo = memoMap[id];
      if (memo && !options?.skipCache) {
        return memo;
      }

      const res = await memoServiceClient.getMemo({
        id,
      });
      if (!res.memo) {
        throw new Error("Memo not found");
      }

      if (!options?.skipStore) {
        memoMap[id] = res.memo;
        set({ memoMapById: memoMap });
      }
      return res.memo;
    },
    getMemoById: (id: number) => {
      return get().memoMapById[id];
    },
    getOrFetchMemoByName: async (name: string) => {
      const memoMap = get().memoMapById;
      const memo = Object.values(memoMap).find((memo) => memo.name === name);
      if (memo) {
        return memo;
      }

      const res = await memoServiceClient.getMemoByName({
        name,
      });
      if (!res.memo) {
        throw new Error("Memo not found");
      }

      memoMap[res.memo.id] = res.memo;
      set({ memoMapById: memoMap });
      return res.memo;
    },
    getMemoByName: (name: string) => {
      const memoMap = get().memoMapById;
      return Object.values(memoMap).find((memo) => memo.name === name);
    },
    createMemo: async (request: CreateMemoRequest) => {
      const { memo } = await memoServiceClient.createMemo(request);
      if (!memo) {
        throw new Error("Memo not found");
      }

      const memoMap = get().memoMapById;
      memoMap[memo.id] = memo;
      set({ memoMapById: memoMap });
      return memo;
    },
    updateMemo: async (update: Partial<Memo>, updateMask: string[]) => {
      const { memo } = await memoServiceClient.updateMemo({
        memo: update,
        updateMask,
      });
      if (!memo) {
        throw new Error("Memo not found");
      }

      const memoMap = get().memoMapById;
      memoMap[memo.id] = memo;
      set({ memoMapById: memoMap });
      return memo;
    },
    deleteMemo: async (id: number) => {
      await memoServiceClient.deleteMemo({
        id: id,
      });

      const memoMap = get().memoMapById;
      delete memoMap[id];
      set({ memoMapById: memoMap });
    },
  })),
);

export const useMemoList = () => {
  const memoStore = useMemoStore();
  const memos = Object.values(memoStore.getState().memoMapById);

  const reset = () => {
    memoStore.setState({ memoMapById: {} });
  };

  const size = () => {
    return Object.keys(memoStore.getState().memoMapById).length;
  };

  return {
    value: memos,
    reset,
    size,
  };
};
