

import { ResponseType } from "axios";
import { useMemo, useState } from "react";
import { toast } from "react-toastify";
import useSWR, { SWRConfiguration, mutate } from "swr";
import useSWRMutation from "swr/mutation";
import { api } from "./config";

type QueryParams = Record<string, string | number | boolean | undefined | null>;

interface UseQueryOptions extends SWRConfiguration {
  skip?: boolean;
}

export function useQuery<TData = unknown>(
  url: string | null,
  params?: QueryParams,
  options: UseQueryOptions = {}
) {
  const { skip, ...swrOptions } = options;

  const {
    data,
    error,
    isLoading,
    isValidating,
    mutate: refetch,
  } = useSWR<{ data: TData }>(
    url && !skip ? [url, params] : null,
    ([url, params]) => api.get(url, { params }),
    {
      revalidateOnFocus: false,
      ...swrOptions,
    }
  );

  return {
    data: data?.data,
    error,
    isLoading,
    isValidating,
    refetch,
  };
}

interface PaginatorResponse {
  current_page: number;
  per_page: number;
  total_count: number;
}

interface PaginatedData<TItem> {
  list?: TItem[];
  paginator?: PaginatorResponse;
}

export function useQueryPagination<TItem = unknown>(
  url: string | null,
  params?: QueryParams,
  options: UseQueryOptions = {}
) {
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState((params?.per_page as number) || 10);

  const paramsString = JSON.stringify(params);

  const queryParams = useMemo(
    () => ({
      page,
      per_page: pageSize,
      ...(params || {}),
    }),
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [page, pageSize, paramsString]
  );

  const swr = useQuery<PaginatedData<TItem>>(url, queryParams, options);

  const paginationProps = useMemo(() => {
    const paginator = (swr.data as PaginatedData<TItem>)?.paginator;

    return {
      currentPage: paginator?.current_page ?? page,
      pageSize: paginator?.per_page ?? pageSize,
      total: paginator?.total_count ?? 0,
      onChange: (newPage: number, newPageSize?: number) => {
        setPage(newPage);
        if (newPageSize && newPageSize !== pageSize) {
          setPageSize(newPageSize);
        }
      },
    };
  }, [swr.data, page, pageSize]);

  const dataWithPaginator = swr.data as PaginatedData<TItem>;
  const dataArray = dataWithPaginator?.list ?? (swr.data as TItem[]) ?? [];

  return {
    ...swr,
    pagination: paginationProps,
    data: dataArray,
    paginator: dataWithPaginator?.paginator,
  };
}

interface MutationResponse {
  message?: string;
  data?: unknown;
}

export function useMutation<
  TResponse extends MutationResponse = MutationResponse,
  TData = unknown
>(url: string, responseType: ResponseType = "json") {

  const createMutation = useSWRMutation(
    url,
    async (_key: string, { arg }: { arg: TData }) => {
      try {
        const response = await api.post<TResponse>(url, arg, { responseType });
        await mutate(url);
        if (response.data?.message) {
          toast.success(response.data.message);
        }
        return response;
      } catch (error) {
        console.error("Error creating resource:", error);
        if (error instanceof Error) {
          toast.error(error.message || "Failed to create resource");
        } else {
          toast.error("An unexpected error occurred");
        }
        throw error;
      }
    }
  );

  const updateMutation = useSWRMutation(
    url,
    async (
      _key: string,
      { arg }: { arg: { data: Partial<TData>; id: string | number } }
    ) => {
      try {
        const response = await api.post<TResponse>(`${url}/${arg.id}`, {
          ...arg.data,
          _method: "PUT",
        });
        await mutate(url);
        if (response.data?.message) {
          toast.success(response.data.message);
        }
        return response;
      } catch (error) {
        console.error("Error updating resource:", error);
        if (error instanceof Error) {
          toast.error(error.message || "Failed to update resource");
        } else {
          toast.error("An unexpected error occurred");
        }
        throw error;
      }
    }
  );

  const deleteMutation = useSWRMutation(
    url,
    async (_key: string, { arg }: { arg: string | number }) => {
      try {
        const response = await api.delete<TResponse>(`${url}/${arg}`);
        await mutate(url);
        if (response.data?.message) {
          toast.success(response.data.message);
        }
        return response;
      } catch (error) {
        console.error("Error deleting resource:", error);
        if (error instanceof Error) {
          toast.error(error.message || "Failed to delete resource");
        } else {
          toast.error("An unexpected error occurred");
        }
        throw error;
      }
    }
  );

  return {
    create: createMutation.trigger,
    update: (data: Partial<TData>, id: string | number) =>
      updateMutation.trigger({ data, id }),
    remove: deleteMutation.trigger,

    isLoading:
      createMutation.isMutating ||
      updateMutation.isMutating ||
      deleteMutation.isMutating,

    isCreating: createMutation.isMutating,
    isUpdating: updateMutation.isMutating,
    isDeleting: deleteMutation.isMutating,

    error: createMutation.error || updateMutation.error || deleteMutation.error,
  } as const;
}
