import type { IMenuItem } from "@ax/types";

const isEmptyObj = (obj: any): boolean => {
	for (const key in obj) {
		if (Object.hasOwn(obj, key)) return false;
	}
	return true;
};

const deepClone = (obj: any) => JSON.parse(JSON.stringify(obj));

const isSelectedEditorID = (element: any, id: number): boolean => element.editorID === id;

const resetMultipleValues = (containerValue: any) => {
	const { id } = containerValue;
	const resetValue = Object.keys(containerValue)
		.map((option: any) => {
			const optionValue = containerValue[option];
			const nullOptionKeys =
				typeof optionValue === "object" &&
				Object.keys(optionValue)
					.filter(
						(key: string) => key === "component" || key === "editorID" || key === "id" || key === "parentEditorID",
					)
					.map((cleanKey: string) => ({ [cleanKey]: optionValue[cleanKey] }))
					.reduce((accumulator, currValue) => {
						return {
							...accumulator,
							...currValue,
						};
					});
			return { [option]: nullOptionKeys };
		})
		.reduce((optionAcc, optionValue) => {
			return {
				...optionAcc,
				...optionValue,
			};
		});

	return {
		...resetValue,
		...(id && id),
	};
};

const filterDuplicatedValues = (arr: any) =>
	arr.reduce((acc: any, current: any) => {
		const item = acc.find((item: any) => {
			return !!item.value === current.value;
		});
		return item ? acc : acc.concat([current]);
	}, []);

const areEquals = (arr1: any, arr2: any) => arr1.every((i: any) => arr2.includes(i));

const removeMenuEditorIds = (menu: IMenuItem[]) =>
	menu.map((item: IMenuItem) => {
		delete item.editorID;
		return item;
	});

const sortBy = (field: string, desc: boolean) => {
	const sortOrder = desc ? -1 : 1;
	return (a: any, b: any) => {
		const result = a[field] < b[field] ? -1 : a[field] > b[field] ? 1 : 0;
		return result * sortOrder;
	};
};

const getNullValue = (containerValue: any, hasMultipleOptions: boolean) => {
	return hasMultipleOptions
		? resetMultipleValues(containerValue)
		: { component: containerValue.component, editorID: containerValue.editorID };
};

const removeEditorIds = (editorContent: any) => {
	const cleanEditorContent = { ...editorContent };
	delete cleanEditorContent.editorID;
	const keys = Object.keys(cleanEditorContent);

	keys.forEach((objKey: string) => {
		const objValue = cleanEditorContent[objKey];
		if (objValue && typeof objValue === "object") {
			if (objValue["editorID"]) {
				delete objValue.editorID;
			}
		}
	});

	return cleanEditorContent;
};

const trimObject = (obj: any) => {
	Object.keys(obj).forEach((key) => {
		if (typeof obj[key] === "string") {
			obj[key] = obj[key].trim();
		}
		if (obj[key] && typeof obj[key] === "object") {
			obj[key] = trimObject(obj[key]);
		}
	});
	return obj;
};

const hasProps = (obj: Record<string, unknown>, props: string[]): boolean => {
	return !!props && props.map((prop) => Object.hasOwn(obj, prop)).filter(Boolean).length === props.length;
};

const encodeData = (data: Record<string, string | number | boolean>): string =>
	Object.entries(data)
		.map((pair) => pair.map(encodeURIComponent).join("="))
		.join("&");

const findObjectValue = (obj: any, key: string): any | undefined => {
	if (obj == null) return undefined;

	if (typeof obj === "object") {
		if (key in obj) {
			return obj[key];
		}

		for (const value of Array.isArray(obj) ? obj : Object.values(obj)) {
			const found = findObjectValue(value, key);
			if (found !== undefined) return found;
		}
	}

	return undefined;
};

const removeDuplicatesByProperty = <T, K extends keyof T>(items: T[], key: K): T[] => {
	const seen = new Set<T[K]>();
	return items.filter((item) => {
		const value = item[key];
		if (seen.has(value)) {
			return false;
		}
		seen.add(value);
		return true;
	});
};

export {
	isEmptyObj,
	deepClone,
	isSelectedEditorID,
	resetMultipleValues,
	filterDuplicatedValues,
	areEquals,
	removeMenuEditorIds,
	sortBy,
	getNullValue,
	removeEditorIds,
	trimObject,
	hasProps,
	encodeData,
	findObjectValue,
	removeDuplicatesByProperty,
};
