import { assert } from "tsafe/assert";
import { is } from "tsafe/is";

//Warning: Be mindful that because of array this is not idempotent.
export function deepAssign(
	params: {
		target: Record<string, unknown>;
		source: Record<string, unknown>;
	}
) {

	const { target, source } = params;

	Object.keys(source).forEach(key => {
		var dereferencedSource = source[key];

		if (
			target[key] === undefined ||
			!(dereferencedSource instanceof Object)
		) {

			Object.defineProperty(
				target,
				key,
				{
					"enumerable": true,
					"value": dereferencedSource
				}
			);

			return;
		}

		const dereferencedTarget = target[key];

		if (dereferencedSource instanceof Array) {

			assert(is<unknown[]>(dereferencedTarget));
			assert(is<unknown[]>(dereferencedSource));

			dereferencedSource.forEach(entry => dereferencedTarget.push(entry));

			return;
		}

		assert(is<Record<string, unknown>>(dereferencedTarget));
		assert(is<Record<string, unknown>>(dereferencedSource));

		deepAssign({
			"target": dereferencedTarget,
			"source": dereferencedSource
		});

	});

}