/* eslint-disable @typescript-eslint/no-explicit-any */
import { tagsForUrl } from "@/utils/cacheTags";
import { useUserStore } from "@/store/userStore";
import { PaginationProps } from "antd";
import axios, { AxiosError, AxiosResponse, ResponseType } from "axios";
import { useTranslations } from "next-intl";
import React, { useMemo, useState } from "react";
import { BsChevronRight } from "react-icons/bs";
import { toast } from "react-toastify";
import useSWR, { mutate, SWRConfiguration, unstable_serialize } from "swr";
import { api } from "./config";

// Types
export interface ApiErrorResponse {
  message: string;
  [key: string]: any;
}

export interface FetcherError extends Error {
  status?: number;
  info?: any;
}

export interface Paginator {
  current_page: number;
  per_page: number;
  total_count: number;
}

export interface PaginatedResponse<T> {
  list: T[];
  paginator: Paginator;
}

// export interface PaginationProps {
//   current: number;
//   pageSize: number;
//   total: number;
//   showSizeChanger: boolean;
//   showTotal: (total: number) => React.ReactNode;
//   onChange: (page: number, pageSize?: number) => void;
// }

// Utility function to handle API errors consistently
const handleApiError = (error: unknown): never => {
  const axiosError = error as AxiosError<ApiErrorResponse>;
  const message =
    axiosError.response?.data?.message || axiosError.message || "An error occurred";

  const fetcherError: FetcherError = new Error(message);
  fetcherError.status = axiosError.response?.status;
  fetcherError.info = axiosError.response?.data;

  console.error("API Error:", {
    status: axiosError.response?.status,
    url: axiosError.config?.url,
    message,
    response: axiosError.response?.data,
  });

  toast.error(message);

  if (axiosError.response?.status === 401) {
    useUserStore.getState().logout();
    window.location.href = "/login";
  }
  if (axiosError.response?.status === 404) {
    window.location.href = "/not-found";
  }

  throw fetcherError;
};

type ApiResponseShape = {
  message?: string;
  status?: boolean;
  success?: boolean;
  code?: number;
  data?: any;
};

const handleMutationResponse = <TResponse extends ApiResponseShape>(
  response: AxiosResponse<TResponse>
) => {
  const { status, data } = response;
  const effectiveCode = data?.code ?? status;
  const isSuccess =
    status >= 200 &&
    status < 300 &&
    data?.status !== false &&
    data?.success !== false &&
    effectiveCode !== 400 &&
    effectiveCode !== 500;

  if (!isSuccess) {
    throw new Error(data?.message || "Request failed");
  }

  return data;
};

const revalidateMutationKeys = async (url: string) => {
  const serializedArrayKeyPrefix = unstable_serialize([url]);

  // Content changed: invalidate Next's tag-based Data Cache so the public
  // homepage/listings show the edit immediately (shared across all server
  // instances via revalidateTag). Hitting the /api/revalidate route handler
  // rather than a Server Action avoids the "Failed to find Server Action"
  // build-mismatch error. Fire-and-forget — must never block or fail the save.
  const tags = tagsForUrl(url);
  console.info("[cache] invalidation requested", url, tags);
  fetch("/api/revalidate", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ tags }),
  }).catch((error) => {
    console.warn("[cache] revalidate failed", error);
  });

  await mutate((key) => {
    if (key === url) return true;
    if (Array.isArray(key)) return key[0] === url;
    if (typeof key === "string") {
      return key.startsWith(serializedArrayKeyPrefix);
    }
    return false;
  });
};

// Generic fetcher function
const fetcher = async (url: string, params?: Record<string, any>) => {
  try {
    const response = await api.get(url, {
      params,
      headers: {
        "Cache-Control": "no-cache",
        Pragma: "no-cache",
      },
    });
    return response.data;
  } catch (error) {
    handleApiError(error);
  }
};

// Generic useQuery hook with improved type safety
export function useQuery(
  url: string | null,
  params?: Record<string, any>,
  options: SWRConfiguration = {},
  skip?: boolean
) {
  const {
    data,
    error,
    isLoading,
    isValidating,
    mutate: refetch,
  } = useSWR(
    url && !skip ? [url, params] : null,
    ([url, params]) => fetcher(url, params),
    {
      revalidateOnFocus: false,
      ...options,
    }
  );

  return {
    data,
    error,
    isLoading,
    isValidating,
    refetch,
  };
}

export function useQueryPagination(
  url: string | null,
  params: Record<string, any> = {},
  options: SWRConfiguration = {},
  skip?: boolean
) {
  const [page, setPage] = useState(1);
  const [pageSize, setPageSize] = useState(params?.per_page || 10);
  const t = useTranslations();

  const queryParams = useMemo(
    () => ({
      page,
      per_page: pageSize,
      ...params,
    }),
    [page, pageSize, params]
  );

  const swr = useQuery(url, queryParams, options, skip);

  const paginationProps = useMemo<PaginationProps>(
    () => ({
      current: swr.data?.data?.paginator?.current_page ?? page,
      pageSize,
      total: swr.data?.data?.paginator?.total_count ?? 0,
      showSizeChanger: false,
      showTotal: () => null,
      // showSizeChanger: (swr.data?.data?.paginator?.total_count ?? 0) > 10,
      // showTotal: (total) => (
      //   <span className="font-medium">{`Total ${total} items`}</span>
      // ),
      onChange: (newPage: number, newPageSize?: number) => {
        setPage(newPage);
        if (newPageSize && newPageSize !== pageSize) {
          setPageSize(newPageSize);
        }
      },
      itemRender: (page, type, originalElement) => {
        if (type === "prev")
          return (
            <button
              className="flex items-center gap-2 px-4 border rounded-xl !text-fontColorSecondary hover:border-primary-500 hover:!text-primary-500 disabled:opacity-50 disabled:cursor-not-allowed"
              disabled={page === 1}
            >
              <BsChevronRight className="ltr:rotate-180" />
              {t("home.previous")}
            </button>
          );
        if (type === "next")
          return (
            <button className="flex items-center gap-2 px-4 border rounded-xl !text-fontColorSecondary hover:border-primary-500 hover:!text-primary-500 disabled:opacity-50 disabled:cursor-not-allowed">
              {t("common.next")}
              <BsChevronRight className="rtl:rotate-180" />
            </button>
          );
        return originalElement;
      },
    }),
    [
      swr.data?.data?.paginator?.current_page,
      swr.data?.data?.paginator?.total_count,
      page,
      pageSize,
    ]
  );

  return {
    ...swr,
    pagination: paginationProps,
    data: swr.data?.data?.list ?? swr.data?.data ?? [],
    paginator: swr.data?.data?.paginator,
  };
}

// Generic useMutation hook with improved type safety
export function useMutation<
  TResponse extends {
    message?: string;
    data?: any;
    code?: number;
    status?: boolean;
  },
  TData = unknown
>(url: string, responseType: ResponseType = "json") {
  const [isLoading, setIsLoading] = React.useState(false);

  const create = async (data: TData) => {
    try {
      setIsLoading(true);
      const response = await api.post<TResponse>(url, data, { responseType });
      const responseData = handleMutationResponse(response);
      await revalidateMutationKeys(url);
      responseData?.message && toast.success(responseData.message);
      return responseData;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        handleApiError(error);
      } else {
        toast.error((error as Error).message || "An error occurred");
      }
    } finally {
      setIsLoading(false);
    }
  };

  const update = async (data: Partial<TData>, id: string | number) => {
    try {
      setIsLoading(true);
      const response = await api.post<TResponse>(`${url}/${id}`, {
        ...data,
        _method: "PUT",
      });
      const responseData = handleMutationResponse(response);
      await revalidateMutationKeys(url);
      responseData?.message && toast.success(responseData.message);
      return responseData;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        handleApiError(error);
      } else {
        toast.error((error as Error).message || "An error occurred");
      }
    } finally {
      setIsLoading(false);
    }
  };

  const updateImageMedia = async (
    data: Partial<TData>,
    id: string | number
  ) => {
    try {
      setIsLoading(true);
      const response = await api.post<TResponse>(`${url}/${id}`, {
        ...data,
        _method: "POST",
      });
      const responseData = handleMutationResponse(response);
      await revalidateMutationKeys(url);
      responseData?.message && toast.success(responseData.message);
      return responseData;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        handleApiError(error);
      } else {
        toast.error((error as Error).message || "An error occurred");
      }
    } finally {
      setIsLoading(false);
    }
  };

  const updateAll = async (data: Record<string, any>, id: string | number) => {
    try {
      setIsLoading(true);

      const payload = {
        ...data,
        _method: "put",
      };

      const response = await api.post<TResponse>(`${url}/${id}`, payload, {
        headers: { "Content-Type": "application/json" },
      });

      const responseData = handleMutationResponse(response);
      await revalidateMutationKeys(url);
      responseData?.message && toast.success(responseData.message);
      return responseData;
    } catch (error) {
      if (axios.isAxiosError(error)) {
        handleApiError(error);
      } else {
        toast.error((error as Error).message || "An error occurred");
      }
    } finally {
      setIsLoading(false);
    }
  };

  const remove = async (id: string | number): Promise<void> => {
    try {
      setIsLoading(true);
      const response = await api.delete(`${url}/${id}`);
      const responseData = handleMutationResponse(response);
      await revalidateMutationKeys(url);
      responseData?.message && toast.success(responseData.message);
    } catch (error) {
      if (axios.isAxiosError(error)) {
        handleApiError(error);
      } else {
        toast.error((error as Error).message || "An error occurred");
      }
    } finally {
      setIsLoading(false);
    }
  };

  return {
    create,
    update,
    remove,
    updateAll,
    updateImageMedia,
    isLoading,
  } as const;
}
