import { ActionTypes, IdObj } from "../types/utils";
import { identify } from "../utils";
import { initialState } from "./initial";

/* Types */
export type SelectionState = {
  ids: Set<string>;
  anchor: string | null;
  mostRecent: string | null;
};

/* Actions */
export const actions = {
  clear: () => ({ type: "SELECTION_CLEAR" as const }),

  only: (id: string | IdObj) => ({
    type: "SELECTION_ONLY" as const,
    id: identify(id),
  }),

  add: (id: string | string[] | IdObj | IdObj[]) => ({
    type: "SELECTION_ADD" as const,
    ids: (Array.isArray(id) ? id : [id]).map(identify),
  }),

  remove: (id: string | string[] | IdObj | IdObj[]) => ({
    type: "SELECTION_REMOVE" as const,
    ids: (Array.isArray(id) ? id : [id]).map(identify),
  }),

  set: (args: {
    ids: Set<string>;
    anchor: string | null;
    mostRecent: string | null;
  }) => ({
    type: "SELECTION_SET" as const,
    ...args,
  }),

  mostRecent: (id: string | null | IdObj) => ({
    type: "SELECTION_MOST_RECENT" as const,
    id: id === null ? null : identify(id),
  }),

  anchor: (id: string | null | IdObj) => ({
    type: "SELECTION_ANCHOR" as const,
    id: id === null ? null : identify(id),
  }),
};

/* Reducer */
export function reducer(
  state: SelectionState = initialState()["nodes"]["selection"],
  action: ActionTypes<typeof actions>
): SelectionState {
  const ids = state.ids;
  switch (action.type) {
    case "SELECTION_CLEAR":
      return { ...state, ids: new Set() };
    case "SELECTION_ONLY":
      return { ...state, ids: new Set([action.id]) };
    case "SELECTION_ADD":
      if (action.ids.length === 0) return state;
      action.ids.forEach((id) => ids.add(id));
      return { ...state, ids: new Set(ids) };
    case "SELECTION_REMOVE":
      if (action.ids.length === 0) return state;
      action.ids.forEach((id) => ids.delete(id));
      return { ...state, ids: new Set(ids) };
    case "SELECTION_SET":
      return {
        ...state,
        ids: action.ids,
        mostRecent: action.mostRecent,
        anchor: action.anchor,
      };
    case "SELECTION_MOST_RECENT":
      return { ...state, mostRecent: action.id };
    case "SELECTION_ANCHOR":
      return { ...state, anchor: action.id };
    default:
      return state;
  }
}
