import { computed, onMounted, ref } from "vue";
import { watchDebounced } from "@vueuse/core";
import { useAlert } from "./alert.js";
import { buildRequiredInputFields } from "./graphqlOperation.js";
import { useGraphqlModelOperation } from "./graphqlModelOperation.js";
import pLimit from "p-limit";
import { arrayWrap } from "../utils/array.js";
export function useGraphqlModel(props) {
  const alert = useAlert();
  const items = ref([]);
  const itemsLength = ref(0);
  const search = ref();
  const currentOptions = ref();
  function setSearch(keyword) {
    search.value = keyword;
  }
  const isLoading = ref(false);
  const { operationCreate, operationUpdate, operationDelete, operationRead, operationReadPageable, operationSearch } = useGraphqlModelOperation(props);
  function keyToField(key) {
    const parts = key.split(".");
    if (parts.length <= 1) return key;
    let lastValue = parts.pop();
    let result = {};
    for (let i = parts.length - 1; i >= 0; i--) {
      if (i == parts.length - 1) result = { [parts[i]]: [lastValue] };
      else result = { [parts[i]]: result };
    }
    return result;
  }
  const fields = computed(() => {
    const tmpFields = [];
    const fieldsProps = props.fields || [];
    let requiredInputFields = [];
    if (props.headers && props.headers.length > 0) {
      props.headers.forEach((header) => {
        if (!fieldsProps.includes(header.key)) tmpFields.push(keyToField(header.key));
      });
    }
    if (canUpdate.value) {
      requiredInputFields = buildRequiredInputFields(operationUpdate.value?.variables);
    }
    return [.../* @__PURE__ */ new Set([...fieldsProps, ...tmpFields, ...requiredInputFields])];
  });
  const canServerPageable = computed(() => {
    return !!operationReadPageable.value && (!search.value || !!search.value && canServerSearch.value);
  });
  const canServerSearch = computed(() => {
    return !!operationSearch.value;
  });
  const canCreate = computed(() => {
    return !!operationCreate.value;
  });
  const canUpdate = computed(() => {
    return !!operationUpdate.value;
  });
  const canDelete = computed(() => {
    return !!operationDelete.value;
  });
  function createItem(item, callback, importing = false) {
    isLoading.value = true;
    return operationCreate.value?.call(fields.value, { input: item }).then((result) => {
      if (canServerPageable.value) {
        if (!importing) loadItems(currentOptions.value);
      } else items.value.push(result);
      if (callback && callback.setData) callback.setData(result);
    }).catch((error) => {
      alert?.addAlert({ alertType: "error", message: error });
    }).finally(() => {
      if (!importing) isLoading.value = false;
      if (callback) callback.done();
    });
  }
  function importItems(importItems2, callback) {
    isLoading.value = true;
    const limit = pLimit(50);
    const importPromises = importItems2.map(
      (item) => limit(
        () => (item[props.modelKey || "id"] ? operationUpdate.value?.call(fields.value, { input: item }).then((result) => {
          if (!result) {
            return createItem(Object.assign({}, props.initialData, item), void 0, true);
          }
        }) : createItem(Object.assign({}, props.initialData, item), void 0, true)).catch((error) => {
          alert?.addAlert({ alertType: "error", message: error });
        })
      )
    );
    Promise.all(importPromises).finally(() => {
      isLoading.value = false;
      reload();
      if (callback) callback.done();
    });
  }
  function updateItem(item, callback) {
    isLoading.value = true;
    return operationUpdate.value?.call(fields.value, { input: item }).then((result) => {
      if (canServerPageable.value) loadItems(currentOptions.value);
      else {
        const index = items.value.findIndex((item2) => item2[props.modelKey || "id"] === result[props.modelKey || "id"]);
        if (index !== -1) {
          items.value[index] = result;
        }
      }
      if (callback && callback.setData) callback.setData(result);
    }).catch((error) => {
      alert?.addAlert({ alertType: "error", message: error });
    }).finally(() => {
      isLoading.value = false;
      if (callback) callback.done();
    });
  }
  function deleteItem(item, callback) {
    isLoading.value = true;
    return operationDelete.value?.call(fields.value, { input: item }).catch((error) => {
      alert?.addAlert({ alertType: "error", message: error });
    }).finally(() => {
      isLoading.value = false;
      reload();
      if (callback) callback.done();
    });
  }
  function loadItems(options) {
    currentOptions.value = options;
    if (canServerPageable.value) {
      const pageableVariable = {
        page: options.page,
        perPage: options.itemsPerPage,
        sortBy: options.sortBy
      };
      isLoading.value = true;
      return operationReadPageable.value?.call([{ data: fields.value }, "meta"], Object.assign({}, props.modelBy, { pageable: pageableVariable })).then((result) => {
        items.value = result.data;
        itemsLength.value = result.meta.totalItems;
      }).catch((error) => {
        items.value = [];
        itemsLength.value = 0;
        alert?.addAlert({ alertType: "error", message: error });
      }).finally(() => {
        isLoading.value = false;
      });
    }
  }
  function reload() {
    if (canServerPageable.value) {
      if (currentOptions.value) return loadItems(currentOptions.value);
    } else {
      isLoading.value = true;
      return operationRead.value?.call(fields.value, props.modelBy).then((result) => {
        items.value = arrayWrap(result);
      }).catch((error) => {
        items.value = [];
        alert?.addAlert({ alertType: "error", message: error });
      }).finally(() => {
        isLoading.value = false;
      });
    }
  }
  watchDebounced([() => props.modelName, () => props.modelBy, () => props.modelKey, canServerPageable], () => {
    reload();
  }, { deep: true, debounce: 500, maxWait: 500 });
  onMounted(() => {
    if (!canServerPageable.value) reload();
  });
  return {
    items,
    itemsLength,
    search,
    setSearch,
    currentOptions,
    operationCreate,
    operationUpdate,
    operationDelete,
    operationRead,
    operationReadPageable,
    operationSearch,
    fields,
    canServerPageable,
    canServerSearch,
    canCreate,
    canUpdate,
    canDelete,
    createItem,
    importItems,
    updateItem,
    deleteItem,
    loadItems,
    reload,
    isLoading
  };
}
