/** @file
 * @author Edouard DUPIN
 * @copyright 2024, Edouard DUPIN, all right reserved
 * @license PROPRIETARY (see license file)
 */
import { DependencyList, useCallback, useEffect, useState } from 'react';

import { RestErrorResponse } from '@/back-api';
import { useToastAPIError } from '@/utils/toastHook';
import { isNullOrUndefined } from '@/utils/validator';

export type DataStoreType<TYPE> = {
  isLoading: boolean;
  error: RestErrorResponse | undefined;
  data: TYPE[];
  get: <MODEL>(value: MODEL, key?: string) => TYPE | undefined;
  gets: <MODEL>(value: MODEL[] | undefined, key?: string) => TYPE[];
  update: (request: Promise<TYPE>, key?: string) => Promise<TYPE>;
  updateRaw: (data: TYPE, key?: string) => void;
  remove: (id: number | string, request: Promise<void>, key?: string) => void;
};

export const useDataStore = <TYPE>(
  {
    primaryKey = 'id',
    restApiName,
    available = true,
    getsCall,
  }: {
    restApiName?: string;
    primaryKey?: string;
    available?: boolean;
    getsCall: () => Promise<TYPE[]>;
  },
  deps: DependencyList = []
): DataStoreType<TYPE> => {
  const [isLoading, setIsLoading] = useState(true);
  const [error, setError] = useState<RestErrorResponse | undefined>(undefined);
  const [data, setData] = useState<TYPE[]>([]);
  const toastAPIError = useToastAPIError();

  // on instantiation ==> call the request of the data...
  useEffect(() => {
    setError(undefined);
    setIsLoading(true);
    if (!available) {
      console.log(`[${restApiName}] NOT call data. service not available`);
      return;
    }
    console.log(`[${restApiName}] call data ...`);
    getsCall()
      .then((response: TYPE[]) => {
        /*console.log(
          `[${restApiName}] getData Response: ${JSON.stringify(response, null, 2)}`
        );*/
        setData(response);
        setError(undefined);
        setIsLoading(false);
      })
      .catch((error: RestErrorResponse) => {
        toastAPIError(error);
        console.log(
          `[${restApiName}] catch error: ${JSON.stringify(error, null, 2)}`
        );
        setError(error);
        setIsLoading(false);
      });
  }, [setIsLoading, setData, ...deps]);

  const get = useCallback(
    <MODEL>(value: MODEL, key?: string): TYPE | undefined => {
      const keyValue = key ?? primaryKey;
      for (let iii = 0; iii < data.length; iii++) {
        if (data[iii][keyValue] === value) {
          return data[iii];
        }
      }
      return undefined;
    },
    [data]
  );
  const gets = useCallback(
    <MODEL>(value: MODEL[] | undefined, key?: string): TYPE[] => {
      const keyValue = key ?? primaryKey;
      const out: TYPE[] = [];
      if (isNullOrUndefined(value)) {
        return out;
      }
      for (let iii = 0; iii < data.length; iii++) {
        if (value.includes(data[iii][keyValue])) {
          out.push(data[iii]);
        }
      }
      return out;
    },
    [data]
  );

  const updateRaw = useCallback(
    (responseData: TYPE, key?: string): void => {
      const keyValue = key ?? primaryKey;
      const filterData = data.filter(
        (localData: TYPE) => localData[keyValue] !== responseData[keyValue]
      );
      filterData.push(responseData);
      setData(filterData);
    },
    [data, setData]
  );

  const update = useCallback(
    (request: Promise<TYPE>, key?: string): Promise<TYPE> => {
      return new Promise((resolve, rejects) => {
        request
          .then((responseData: TYPE) => {
            updateRaw(responseData, key);
            resolve(responseData);
          })
          .catch((error: RestErrorResponse) => {
            toastAPIError(error);
            rejects(error);
          });
      });
    },
    [data, updateRaw]
  );
  const remove = useCallback(
    (id: number | string, request: Promise<void>, key?: string) => {
      const keyValue = key ?? primaryKey;
      request
        .then(() => {
          const filterData = data.filter(
            (localData: TYPE) => localData[keyValue] !== id
          );
          setData(filterData);
        })
        .catch((error) => {
          toastAPIError(error);
          console.log(
            `catch an error on delete: ... ${JSON.stringify(error, null, 2)}`
          );
        });
    },
    [data, setData]
  );

  return { isLoading, error, data, get, gets, update, updateRaw, remove };
};
