import { computed, onMounted, ref } from "vue";
import { watchDebounced } from "@vueuse/core";
import { useAlert } from "./alert.js";
import { useApiModelOperation } from "./apiModelOperation.js";
import { arrayWrap } from "../utils/array.js";
import pLimit from "p-limit";
export function stripModelByQualifier(modelName) {
  return modelName.split("By")[0].trim();
}
export function buildApiEndpoint(modelName, action) {
  const lowercasedName = modelName.charAt(0).toLowerCase() + modelName.slice(1);
  const base = `/model/${lowercasedName}`;
  return action ? `${base}/${action}` : base;
}
export function useApiModel(props) {
  const alert = useAlert();
  const ops = computed(() => useApiModelOperation(props.modelName));
  const items = ref([]);
  const itemsLength = ref(0);
  const search = ref();
  const currentOptions = ref();
  const isLoading = ref(false);
  function setSearch(keyword) {
    search.value = keyword;
  }
  const canServerPageable = computed(() => true);
  const canServerSearch = computed(() => true);
  const canCreate = computed(() => true);
  const canUpdate = computed(() => true);
  const canDelete = computed(() => true);
  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 || [];
    if (props.headers && props.headers.length > 0) {
      props.headers.forEach((header) => {
        if (!fieldsProps.includes(header.key)) tmpFields.push(keyToField(header.key));
      });
    }
    return [.../* @__PURE__ */ new Set([...fieldsProps, ...tmpFields])];
  });
  function createItem(item, callback, importing = false) {
    isLoading.value = true;
    return ops.value.create(item, fields.value).then((result) => {
      if (canServerPageable.value) {
        if (!importing) loadItems(currentOptions.value);
      } else {
        items.value.push(result);
      }
      if (callback && callback.setData) callback.setData(result);
      return result;
    }).catch((error) => {
      alert?.addAlert({ alertType: "error", message: error?.message || String(error) });
    }).finally(() => {
      if (!importing) isLoading.value = false;
      if (callback) callback.done();
    });
  }
  function importItems(importItemsList, callback) {
    isLoading.value = true;
    const limit = pLimit(50);
    const importPromises = importItemsList.map(
      (item) => limit(
        () => (item[props.modelKey || "id"] ? updateItem(item, void 0).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?.message || String(error) });
        })
      )
    );
    Promise.all(importPromises).finally(() => {
      isLoading.value = false;
      reload();
      if (callback) callback.done();
    });
  }
  function updateItem(item, callback) {
    isLoading.value = true;
    return ops.value.update(item, fields.value).then((result) => {
      if (canServerPageable.value) {
        loadItems(currentOptions.value);
      } else {
        const index = items.value.findIndex((i) => i[props.modelKey || "id"] === result[props.modelKey || "id"]);
        if (index !== -1) {
          items.value[index] = result;
        }
      }
      if (callback && callback.setData) callback.setData(result);
      return result;
    }).catch((error) => {
      alert?.addAlert({ alertType: "error", message: error?.message || String(error) });
    }).finally(() => {
      isLoading.value = false;
      if (callback) callback.done();
    });
  }
  function deleteItem(item, callback) {
    isLoading.value = true;
    return ops.value.delete(item, fields.value).catch((error) => {
      alert?.addAlert({ alertType: "error", message: error?.message || String(error) });
    }).finally(() => {
      isLoading.value = false;
      reload();
      if (callback) callback.done();
    });
  }
  function loadItems(options) {
    currentOptions.value = options;
    isLoading.value = true;
    return ops.value.readPageable(
      fields.value,
      props.modelBy,
      {
        page: options?.page,
        perPage: options?.itemsPerPage,
        sortBy: options?.sortBy
      }
    ).then((result) => {
      items.value = result.data;
      itemsLength.value = result.meta.totalItems;
    }).catch((error) => {
      items.value = [];
      itemsLength.value = 0;
      alert?.addAlert({ alertType: "error", message: error?.message || String(error) });
    }).finally(() => {
      isLoading.value = false;
    });
  }
  function reload() {
    if (currentOptions.value) {
      return loadItems(currentOptions.value);
    } else {
      isLoading.value = true;
      return ops.value.read(
        fields.value,
        props.modelBy
      ).then((result) => {
        items.value = arrayWrap(result);
      }).catch((error) => {
        items.value = [];
        alert?.addAlert({ alertType: "error", message: error?.message || String(error) });
      }).finally(() => {
        isLoading.value = false;
      });
    }
  }
  watchDebounced([() => props.modelName, () => props.modelBy, () => props.modelKey], () => {
    reload();
  }, { deep: true, debounce: 500, maxWait: 500 });
  onMounted(() => {
    reload();
  });
  return {
    items,
    itemsLength,
    search,
    setSearch,
    currentOptions,
    fields,
    canServerPageable,
    canServerSearch,
    canCreate,
    canUpdate,
    canDelete,
    createItem,
    importItems,
    updateItem,
    deleteItem,
    loadItems,
    reload,
    isLoading
  };
}
