import { useQuery, useQueryClient, useMutation } from "@tanstack/react-query";
import axios from "axios";

export const useEntities = <T extends { id: string }>(
  key: string,
  url: string
) => {
  const entities = useQuery<T[], Error>(
    [key],
    async ({ signal }): Promise<T[]> => {
      const { data } = await axios.get<T[]>(url, { signal });
      return data;
    }
  );

  return {
    entities,
  };
};

export const useEntity = <T extends { id: string }>(
  key: string,
  url: string,
  id: string | undefined | null
) => {
  const queryClient = useQueryClient();

  const invalidateActive = () =>
    queryClient.invalidateQueries({
      queryKey: [key, "query"],
      type: "active",
    });

  const entity = useQuery<T, Error>(
    [key, id],
    async ({ signal }): Promise<T> => {
      const { data } = await axios.get<T>(url + "/" + id, { signal });
      return data;
    },
    { enabled: !!id }
  );

  const add = useMutation<T, Error, T, any>(
    async (entity: T): Promise<T> => {
      const { data } = await axios.post<T>(url, entity);
      return data;
    },
    {
      onSuccess: (addedEntity) => {
        queryClient.setQueryData([key, addedEntity.id], addedEntity);
        queryClient.setQueryData([key], (cachedEntities: T[] | undefined) =>
          cachedEntities ? [...cachedEntities, addedEntity] : undefined
        );
        invalidateActive();
      },
    }
  );

  const update = useMutation<T, Error, T, any>(
    async (entity: T): Promise<T> => {
      const { data } = await axios.put<T>(url + "/" + id, entity);
      return data;
    },
    {
      onSuccess: (updatedEntity, variable) => {
        queryClient.setQueryData([key, id], updatedEntity);
        queryClient.setQueryData(
          [key],
          (cachedEntities: T[] | undefined) =>
            cachedEntities?.map((cachedEntity) =>
              cachedEntity.id === variable.id ? updatedEntity : cachedEntity
            )
        );
        invalidateActive();
      },
    }
  );

  const remove = useMutation<string, Error, string, any>(
    async (id: string): Promise<string> => {
      const { data } = await axios.delete(url + "/" + id);
      return data;
    },
    {
      onSuccess: (deletedId) => {
        queryClient.setQueryData([key, id], null);
        queryClient.setQueryData(
          [key],
          (cachedEntities: T[] | undefined) =>
            cachedEntities?.filter(
              (cachedEntity) => cachedEntity.id !== deletedId
            )
        );
        invalidateActive();
      },
    }
  );

  return {
    entity,
    add,
    update,
    remove,
  };
};
