// src/model.ts
var STATUS = {
  ADDED: "added",
  EQUAL: "equal",
  MOVED: "moved",
  DELETED: "deleted",
  UPDATED: "updated"
};

// src/utils.ts
function isEqual(a, b) {
  if (typeof a !== typeof b)
    return false;
  if (Array.isArray(a)) {
    if (a.length !== b.length) {
      return false;
    }
    return a.every((v, i) => JSON.stringify(v) === JSON.stringify(b[i]));
  }
  if (typeof a === "object") {
    return JSON.stringify(a) === JSON.stringify(b);
  }
  return a === b;
}
function isObject(value) {
  return !!value && typeof value === "object" && !Array.isArray(value);
}

// src/object-diff.ts
function getObjectStatus(diff) {
  return diff.some((property) => property.status !== STATUS.EQUAL) ? STATUS.UPDATED : STATUS.EQUAL;
}
function formatSingleObjectDiff(data, status) {
  if (!data) {
    return {
      type: "object",
      status: STATUS.isEqual,
      diff: []
    };
  }
  const diff = [];
  Object.entries(data).forEach(([property, value]) => {
    if (isObject(value)) {
      const subPropertiesDiff = [];
      Object.entries(value).forEach(([subProperty, subValue]) => {
        subPropertiesDiff.push({
          name: subProperty,
          previousValue: status === STATUS.ADDED ? void 0 : subValue,
          currentValue: status === STATUS.ADDED ? subValue : void 0,
          status
        });
      });
      return diff.push({
        property,
        previousValue: status === STATUS.ADDED ? void 0 : data[property],
        currentValue: status === STATUS.ADDED ? value : void 0,
        status,
        subPropertiesDiff
      });
    }
    return diff.push({
      property,
      previousValue: status === STATUS.ADDED ? void 0 : data[property],
      currentValue: status === STATUS.ADDED ? value : void 0,
      status
    });
  });
  return {
    type: "object",
    status,
    diff
  };
}
function getPreviousMatch(previousValue, nextSubProperty) {
  if (!previousValue) {
    return void 0;
  }
  const previousMatch = Object.entries(previousValue).find(
    ([subPreviousKey]) => isEqual(subPreviousKey, nextSubProperty)
  );
  return previousMatch ? previousMatch[1] : void 0;
}
function getValueStatus(previousValue, nextValue) {
  if (isEqual(previousValue, nextValue)) {
    return STATUS.EQUAL;
  }
  return STATUS.UPDATED;
}
function getPropertyStatus(subPropertiesDiff) {
  return subPropertiesDiff.some((property) => property.status !== STATUS.EQUAL) ? STATUS.UPDATED : STATUS.EQUAL;
}
function getDeletedProperties(previousValue, nextValue) {
  if (!previousValue)
    return void 0;
  const prevKeys = Object.keys(previousValue);
  const nextKeys = Object.keys(nextValue);
  const deletedKeys = prevKeys.filter((prevKey) => !nextKeys.includes(prevKey));
  if (deletedKeys.length > 0) {
    return deletedKeys.map((deletedKey) => ({
      property: deletedKey,
      value: previousValue[deletedKey]
    }));
  }
  return void 0;
}
function getSubPropertiesDiff(previousValue, nextValue) {
  const subPropertiesDiff = [];
  let subDiff;
  const deletedMainSubProperties = getDeletedProperties(
    previousValue,
    nextValue
  );
  if (deletedMainSubProperties) {
    deletedMainSubProperties.forEach((deletedProperty) => {
      subPropertiesDiff.push({
        name: deletedProperty.property,
        previousValue: deletedProperty.value,
        currentValue: void 0,
        status: STATUS.DELETED
      });
    });
  }
  Object.entries(nextValue).forEach(([nextSubProperty, nextSubValue]) => {
    const previousMatch = getPreviousMatch(previousValue, nextSubProperty);
    if (!!!previousMatch && !!nextSubProperty) {
      return subPropertiesDiff.push({
        name: nextSubProperty,
        previousValue: void 0,
        currentValue: nextSubValue,
        status: STATUS.ADDED
      });
    }
    if (isObject(nextSubValue)) {
      const data = getSubPropertiesDiff(
        previousMatch,
        nextSubValue
      );
      if (data && data.length > 0) {
        subDiff = data;
      }
    }
    if (previousMatch) {
      subPropertiesDiff.push({
        name: nextSubProperty,
        previousValue: previousMatch,
        currentValue: nextSubValue,
        status: getValueStatus(previousMatch, nextSubValue),
        ...!!subDiff && { subDiff }
      });
    }
  });
  return subPropertiesDiff;
}
function getObjectDiff(prevData, nextData) {
  if (!prevData && !nextData) {
    return {
      type: "object",
      status: STATUS.EQUAL,
      diff: []
    };
  }
  if (!prevData) {
    return formatSingleObjectDiff(nextData, STATUS.ADDED);
  }
  if (!nextData) {
    return formatSingleObjectDiff(prevData, STATUS.DELETED);
  }
  const diff = [];
  Object.entries(nextData).forEach(([nextProperty, nextValue]) => {
    const previousValue = prevData[nextProperty];
    if (!!!previousValue) {
      return diff.push({
        property: nextProperty,
        previousValue: void 0,
        currentValue: nextValue,
        status: STATUS.ADDED
      });
    }
    if (isObject(nextValue)) {
      const subPropertiesDiff = getSubPropertiesDiff(
        previousValue,
        nextValue
      );
      return diff.push({
        property: nextProperty,
        previousValue,
        currentValue: nextValue,
        status: getPropertyStatus(subPropertiesDiff),
        subPropertiesDiff
      });
    }
    return diff.push({
      property: nextProperty,
      previousValue,
      currentValue: nextValue,
      status: getValueStatus(previousValue, nextValue)
    });
  });
  const deletedProperties = getDeletedProperties(prevData, nextData);
  if (deletedProperties) {
    deletedProperties.forEach((deletedProperty) => {
      diff.push({
        property: deletedProperty.property,
        previousValue: deletedProperty.value,
        currentValue: void 0,
        status: STATUS.DELETED
      });
    });
  }
  return {
    type: "object",
    status: getObjectStatus(diff),
    diff
  };
}

// src/list-diff.ts
function formatSingleListDiff(listData, status) {
  return {
    type: "list",
    status,
    diff: listData.map((data, i) => ({
      value: data,
      prevIndex: status === STATUS.ADDED ? null : i,
      newIndex: status === STATUS.ADDED ? i : null,
      indexDiff: null,
      status
    }))
  };
}
function getListStatus(listDiff) {
  return listDiff.some((value) => value.status !== STATUS.EQUAL) ? STATUS.UPDATED : STATUS.EQUAL;
}
var getListDiff = (prevList, nextList) => {
  if (!prevList && !nextList) {
    return {
      type: "list",
      status: STATUS.EQUAL,
      diff: []
    };
  }
  if (!prevList) {
    return formatSingleListDiff(nextList, STATUS.ADDED);
  }
  if (!nextList) {
    return formatSingleListDiff(prevList, STATUS.DELETED);
  }
  const diff = [];
  nextList.forEach((nextValue, i) => {
    const prevIndex = prevList.findIndex(
      (prevValue) => isEqual(prevValue, nextValue)
    );
    const indexDiff = prevIndex === -1 ? null : i - prevIndex;
    if (indexDiff === 0) {
      return diff.push({
        value: nextValue,
        prevIndex,
        newIndex: i,
        indexDiff,
        status: STATUS.EQUAL
      });
    }
    if (prevIndex === -1) {
      return diff.push({
        value: nextValue,
        prevIndex: null,
        newIndex: i,
        indexDiff,
        status: STATUS.ADDED
      });
    }
    return diff.push({
      value: nextValue,
      prevIndex,
      newIndex: i,
      indexDiff,
      status: STATUS.MOVED
    });
  });
  prevList.forEach((prevValue, i) => {
    if (!nextList.some((nextValue) => isEqual(nextValue, prevValue))) {
      return diff.splice(i, 0, {
        value: prevValue,
        prevIndex: i,
        newIndex: null,
        indexDiff: null,
        status: STATUS.DELETED
      });
    }
  });
  return {
    type: "list",
    status: getListStatus(diff),
    diff
  };
};

export { getListDiff, getObjectDiff, isEqual, isObject };