import { IListResponse_DTO, INITIAL_PAGE_INDEX, INITIAL_PAGE_SIZE } from "@shared/contexts";
import { getApiAuthorization as getApiAuthLib } from "../api";
import {
  ICreateOneDataProviderParams,
  ICustomDataProviderParam,
  IDataSource,
  IDownloadPDFDataProviderParams,
  IDownloadPDFDataProviderResponse,
  IFilterItemDataProviderParam,
  IGetListDataProviderParams,
  IGetOneDataProviderParams,
  IPaginationDataProviderParam,
  IRemoveOneDataProviderParams,
  ISortItemDataProviderParam,
  IUpdateOneDataProviderParams,
  IUploadFileDataProviderParam,
} from "../hooks/useDataSource/DataSource";
import { createAxiosInstance, defaultAxiosRequestConfig } from "./axiosInstance";

export const createAxiosDataProvider = (
  apiUrl: string,
  httpClient = createAxiosInstance()
): IDataSource => ({
  name: () => "AxiosDataProvider",

  getApiUrl: () => apiUrl,

  getApiAuthorization: getApiAuthLib,

  getList: async <R>(params: IGetListDataProviderParams): Promise<IListResponse_DTO<R>> => {
    const { resource, quickSearchTerm, pagination, filters = [], sort = [] } = params;

    const url = `${apiUrl}/${resource}`;
    const urlParams = new URLSearchParams();

    const { page, limit } = extractPaginationParams(pagination);
    urlParams.append("page", String(page));
    urlParams.append("limit", String(limit));

    const generatedSort = extractSortParams(sort);
    if (generatedSort.length) {
      urlParams.append("$sort_by", generatedSort.join(","));
    }

    const queryQuickSearch = generateQuickSearch(quickSearchTerm, filters);
    if (queryQuickSearch.length) {
      urlParams.append("q", queryQuickSearch.join(","));
    }

    const queryFilters = extractFilterParams(filters);
    if (queryFilters.length) {
      urlParams.append("$filters", queryFilters.join(","));
    }

    const response = await httpClient.request<IListResponse_DTO<R>>({
      url: `${url}?${urlParams.toString()}`,
      method: "GET",
    });

    return response.data;
  },

  getOne: async <R>(params: IGetOneDataProviderParams): Promise<R> => {
    const { resource, id } = params;

    const response = await httpClient.request<R>({
      url: `${apiUrl}/${resource}/${id}`,
      method: "GET",
    });

    return response.data;
  },

  /*saveOne: async <P, R>(params: ISaveOneDataProviderParams<P>): Promise<R> => {
    const { resource, data, id } = params;

    console.log(params);

    const result = await httpClient.request<R>({
      url: `${apiUrl}/${resource}/${id}`,
      method: "PUT",
      data,
    });

    return result.data;
  },*/

  createOne: async <P, R>(params: ICreateOneDataProviderParams<P>): Promise<R> => {
    const { resource, data } = params;

    const response = await httpClient.request<R>({
      url: `${apiUrl}/${resource}`,
      method: "POST",
      data,
    });

    return response.data;
  },

  updateOne: async <P, R>(params: IUpdateOneDataProviderParams<P>): Promise<R> => {
    const { resource, data, id } = params;

    const response = await httpClient.request<R>({
      url: `${apiUrl}/${resource}/${id}`,
      method: "PUT",
      data,
    });

    return response.data;
  },

  removeOne: async <R>(params: IRemoveOneDataProviderParams) => {
    const { resource, id } = params;

    await httpClient.request<R>({
      url: `${apiUrl}/${resource}/${id}`,
      method: "DELETE",
    });

    return;
  },

  uploadFile: async <R>(params: IUploadFileDataProviderParam): Promise<R> => {
    const { path, file, key, onUploadProgress } = params;
    const url = `${apiUrl}/${path}`;

    const formData = new FormData();
    formData.append(key || "file", file);

    //console.log(file);

    const response = await httpClient.post<R>(url, formData, {
      headers: {
        "Content-Type": "multipart/form-data",
      },
      onUploadProgress,
    });

    return response.data;
  },

  downloadPDF: async (
    params: IDownloadPDFDataProviderParams
  ): Promise<IDownloadPDFDataProviderResponse> => {
    const { url, config } = params;

    const response = await httpClient.get<ArrayBuffer>(url, {
      responseType: "arraybuffer", // Esto es necesario para recibir los datos en formato ArrayBuffer
      ...config,
    });

    // Extraer el nombre del archivo de la cabecera Content-Disposition
    const contentDisposition = response.headers["content-disposition"];
    let filename: string = "downloaded-file.pdf"; // Valor por defecto si no se encuentra el nombre en la cabecera

    if (contentDisposition) {
      const match = contentDisposition.match(/filename="?(.+)"?/);
      if (match && match[1]) {
        filename = match[1];
      }
    }

    // Crear un Blob con los datos descargados
    const filedata = new Blob([response.data], { type: "application/pdf" });

    return {
      filename,
      filedata,
    };
  },

  custom: async <R>(params: ICustomDataProviderParam): Promise<R> => {
    const { url, path, method, responseType, headers, signal, data, ...payload } = params;
    let requestUrl: string;

    if (path) {
      requestUrl = `${apiUrl}/${path}`;
    } else if (url) {
      requestUrl = url;
    } else {
      throw new Error('"url" or "path" param is missing');
    }

    //console.log(apiUrl, path, url, requestUrl.toString());

    // Preparar la respuesta personalizada
    let customResponse;

    // Configurar opciones comunes para la petición
    const config = {
      url: requestUrl.toString(),
      method,
      responseType,
      signal,
      ...payload,
      ...defaultAxiosRequestConfig,
    };

    switch (method) {
      case "put":
      case "post":
      case "patch":
        customResponse = await httpClient.request<R>({
          ...config,
          data,
        });
        break;
      case "delete":
        customResponse = await httpClient.delete<R>(requestUrl.toString(), {
          responseType,
          headers,
          ...payload,
        });
        break;
      default:
        customResponse = await httpClient.get<R>(requestUrl.toString(), {
          responseType,
          signal,
          headers,
          ...payload,
        });
        break;
    }

    return customResponse.data;
  },

  /*getMany: async ({ resource }) => {
    const { body } = await httpClient.request({
      url: `${apiUrl}/${resource}`,
      method: "GET",
      //...defaultRequestConfig,
    });

    return body;
  },*/

  /*create: async ({ resource, values }) => {
    const url = `${apiUrl}/${resource}`;

    const { body } = await httpClient.post(url, values, defaultRequestConfig);

    return body;
  },*/

  /*createMany: async ({ resource, values }) => {
    const response = await Promise.all(
      values.map(async (param) => {
        const { body } = await httpClient.post(
          `${apiUrl}/${resource}`,
          param
          //defaultRequestConfig,
        );
        return body;
      })
    );

    return response;
  },*/

  /*update: async ({ resource, id, values }) => {
    const url = `${apiUrl}/${resource}/${id}`;
    const { body } = await httpClient.patch(url, values, defaultRequestConfig);
    return body;
  },*/

  /*updateMany: async ({ resource, ids, values }) => {
    const response = await Promise.all(
      ids.map(async (id) => {
        const { body } = await httpClient.patch(
          `${apiUrl}/${resource}/${id}`,
          values
          //defaultRequestConfig,
        );
        return body;
      })
    );

    return response;
  },*/

  // removeMany: async ({ resource, ids }) => {
  //   const url = `${apiUrl}/${resource}/bulk-delete`;

  //   const { body } = await httpClient.request({
  //     url,
  //     method: "PATCH",
  //     data: {
  //       ids,
  //     },
  //     //defaultRequestConfig,
  //   });

  //   return body;
  // },

  // upload: async ({ resource, file, onUploadProgress }) => {
  //   const url = `${apiUrl}/${resource}`;
  //   const options = {
  //     //...defaultRequestConfig,
  //     onUploadProgress,
  //     headers: {
  //       //...defaultRequestConfig.headers,
  //       "Content-Type": "multipart/form-data",
  //     },
  //   };

  //   const formData = new FormData();
  //   formData.append("file", file);

  //   const { body } = await httpClient.post(url, formData, options);
  //   return body;
  // },

  /*uploadMany: async ({ resource, values }) => {
		const url = `${apiUrl}/${resource}`;
		const options = {
			//...defaultRequestConfig,
			headers: {
				...defaultRequestConfig.headers,
				'Content-Type': 'multipart/form-data'
			}
		};

		const response = await Promise.all(
			values.map(async (value) => {
				const { body } = await httpClient.post(
					url,
					value,
					options
				);
				return body;
			}),
		);

		return response;
	},*/

  /*custom: async ({ url, method, filters, sort, payload, query, headers }) => {
    let requestUrl = `${url}?`;

    if (sort) {
      const generatedSort = extractSortParams(sort);
      if (generatedSort) {
        const { _sort, _order } = generatedSort;
        const sortQuery = {
          _sort: _sort.join(","),
          _order: _order.join(","),
        };
        requestUrl = `${requestUrl}&${queryString.stringify(sortQuery)}`;
      }
    }

    if (filters) {
      const filterQuery = extractFilterParams(filters);
      requestUrl = `${requestUrl}&${queryString.stringify(filterQuery)}`;
    }

    if (query) {
      requestUrl = `${requestUrl}&${queryString.stringify(query)}`;
    }

    if (headers) {
      httpClient.defaults.headers = {
        ...httpClient.defaults.headers,
        ...headers,
      };
    }

    let axiosResponse;
    switch (method) {
      case "put":
      case "post":
      case "patch":
        axiosResponse = await httpClient[method](url, payload);
        break;
      case "remove":
        axiosResponse = await httpClient.delete(url);
        break;
      default:
        axiosResponse = await httpClient.get(requestUrl);
        break;
    }

    const { data } = axiosResponse;

    return Promise.resolve({ data });
  },*/
});

const extractSortParams = (sort: ISortItemDataProviderParam[] = []) =>
  sort.map(({ field, order }) => `${order === "DESC" ? "-" : "+"}${field}`);

const extractFilterParams = (filters: IFilterItemDataProviderParam[] = []) =>
  filters
    .filter(({ field }) => field !== "q")
    .map(({ field, operator, value }) => `${field}[${operator}]${value}`);

const generateQuickSearch = (
  quickSearchTerm: string[] = [],
  filters: IFilterItemDataProviderParam[] = []
) =>
  filters.find(({ field }) => field === "q")?.value
    ? [filters.find(({ field }) => field === "q")!.value]
    : quickSearchTerm;

const extractPaginationParams = (pagination?: IPaginationDataProviderParam) => {
  const { pageIndex = INITIAL_PAGE_INDEX, pageSize = INITIAL_PAGE_SIZE } = pagination || {};

  return {
    page: pageIndex,
    limit: pageSize,
  };
};
