export const deepClone = <T>(item: T): T => {
  if (item === null || typeof item !== 'object') {
    return item; // Return the value if item is not an object
  }

  // Handle Date
  if (item instanceof Date) {
    return new Date(item.getTime()) as T;
  }

  // Handle Array
  if (Array.isArray(item)) {
    const copy = [];
    for (let i = 0; i < item.length; i += 1) {
      copy[i] = deepClone(item[i]);
    }
    return copy as T;
  }

  // Handle Object
  if (item instanceof Object) {
    const copy: { [key: string]: unknown } = {};

    for (const key in item) {
      if (Object.prototype.hasOwnProperty.call(item, key)) {
        copy[key] = deepClone(item[key]);
      }
    }
    return copy as T;
  }

  // Handle Map
  if ((item as unknown) instanceof Map) {
    const copy = new Map();
    (item as []).forEach((value, key) => {
      copy.set(key, deepClone(value));
    });
    return copy as T;
  }

  // Handle Set
  if ((item as unknown) instanceof Set) {
    const copy = new Set();
    (item as []).forEach((value) => {
      copy.add(deepClone(value));
    });
    return copy as T;
  }

  throw new Error('Unsupported type!');
};

export const isEqual = (obj1: unknown, obj2: unknown): boolean => {
  const getType = (obj: unknown) => Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();

  const areArraysEqual = (obj1$: [], obj2$: []) =>
    obj1$.length === obj2$.length && obj1$.every((val1, index) => isEqual(val1, obj2$[index]));

  const areObjectsEqual = (obj1$: { [key: string]: unknown }, obj2$: { [key: string]: unknown }) => {
    const obj1Keys = Object.keys(obj1$);
    const obj2Keys = Object.keys(obj2$);

    return (
      obj1Keys.length === obj2Keys.length &&
      obj1Keys.every(
        // eslint-disable-next-line no-prototype-builtins
        (key) => obj2$.hasOwnProperty(key) && isEqual(obj1$[key] as unknown, obj2$[key] as unknown),
      )
    );
  };

  const areFunctionsEqual = (obj1$: () => void, obj2$: () => void) => obj1$.toString() === obj2$.toString();
  const arePrimitivesEqual = (obj1$: unknown, obj2$: unknown) =>
    obj1$ === obj2$ || (Number.isNaN(obj1$) && Number.isNaN(obj2$));

  const type = getType(obj1);

  if (type !== getType(obj2)) {
    return false;
  }

  if (type === 'array') {
    return areArraysEqual(obj1 as [], obj2 as []);
  }

  if (type === 'object') {
    return areObjectsEqual(obj1 as { [key: string]: unknown }, obj2 as { [key: string]: unknown });
  }

  if (type === 'function') {
    return areFunctionsEqual(obj1 as () => void, obj2 as () => void);
  }

  return arePrimitivesEqual(obj1, obj2);
};

// todo: replace static slice name to dynamic link to unique folder structure
export const getSliceName = (metaUrl: string) => {
  const rawName = metaUrl
    .replace(/[\\/]/g, '/')
    .split('src/modules/')
    .pop()!
    .replace(/\.[^/.]+$/, '');

  return `@@${rawName}`;
};
