/* eslint-disable react-refresh/only-export-components */
import { type MenuItemProps, type MenuProps } from '@mantine/core';
import { getUUID } from '@warkypublic/artemis-kit';
import { createSyncStore } from '@warkypublic/zustandsyncstore';
import { produce } from 'immer';
import { type ReactNode } from 'react';

export interface MantineBetterMenuInstance {
  id: string;
  items?: Array<MantineBetterMenuInstanceItem>;
  menuProps?: MenuProps;
  renderer?: ReactNode;
  visible: boolean;

  x: number;
  y: number;
}

export interface MantineBetterMenuInstanceItem extends Partial<MenuItemProps> {
  id?: string;
  isDivider?: boolean;
  items?: Array<MantineBetterMenuInstanceItem>;
  label?: string;
  onClick?: (e?: React.MouseEvent<HTMLButtonElement, MouseEvent>) => void;
  onClickAsync?: () => Promise<void>;
  renderer?:
    | ((props: MantineBetterMenuInstanceItem & Record<string, unknown>) => ReactNode)
    | ReactNode;
}

export interface MantineBetterMenuStoreProps {
  menus?: Array<MantineBetterMenuInstance>;
  providerID?: string;
  width?: number;
}

export type MantineBetterMenuStoreState = MantineBetterMenuStoreProps &
  MantineBetterMenuStoreStateOnly;

export interface MantineBetterMenuStoreStateOnly {
  hide: (id: string) => void;
  menus: Array<MantineBetterMenuInstance>;
  setInstanceState: <K extends keyof MantineBetterMenuInstance>(
    instanceID: string,
    key: K,
    value: MantineBetterMenuInstance[K]
  ) => void;
  setState: <K extends keyof MantineBetterMenuStoreState>(
    key: K,
    value: Partial<MantineBetterMenuStoreState[K]>
  ) => void;
  show: (id: string, options?: Partial<MantineBetterMenuInstance>) => void;
}

const { Provider: MantineBetterMenusStoreProvider, useStore: useMantineBetterMenus } =
  createSyncStore<MantineBetterMenuStoreState, MantineBetterMenuStoreProps>(
    (set, get) => ({
      hide: (id: string) => {
        const s = get();
        s.setInstanceState(id, 'visible', false);
      },
      menus: [],
      setInstanceState: (id, key, value) => {
        //@ts-expect-error Type instantiation is excessively deep and possibly infinite.
        set(
          produce((state: MantineBetterMenuStoreState) => {
            const idx = state?.menus?.findIndex((m: MantineBetterMenuInstance) => m.id === id);
            if (idx >= 0) {
              state.menus[idx][key] = value;
            }
          })
        );
      },
      setState: (key, value) => {
        set(
          produce((state) => {
            state[key] = value;
          })
        );
      },
      show: (id: string, options?: Partial<Omit<MantineBetterMenuInstance, 'id'>>) => {
        const s = get();
        const menuIndex = s.menus.findIndex((m) => m.id === id);
        const menu: Partial<MantineBetterMenuInstance> = s.menus[menuIndex]
          ? { ...s.menus[menuIndex] }
          : {};

        Object.assign(menu, options);
        menu.id = menu.id ?? id;
        menu.visible = !(menu.visible ?? false);

        if (menuIndex < 0) {
          s.setState('menus', [...s.menus, menu as MantineBetterMenuInstance]);
        } else {
          set(
            produce((state: MantineBetterMenuStoreState) => {
              if (!state.menus) {
                state.menus = [];
              }
              state.menus[menuIndex] = { ...state.menus[menuIndex], ...menu };
            })
          );
        }
      },
    }),
    (props) => {
      return {
        providerID: props.providerID ?? `MenuStore-${getUUID()}`,
      };
    }
  );

export { MantineBetterMenusStoreProvider, useMantineBetterMenus };
