import { Binary, Decimal128, ObjectId } from "mongodb";

export default class JsonEncoder {
	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	static encode(obj: any): any {
		if (obj instanceof ObjectId) {
			return {
				$type: "ObjectId",
				$value: obj.toHexString(),
				$date: obj.getTimestamp().getTime(),
			};
		}
		if (obj instanceof Date) {
			return {
				$type: "Date",
				$value: obj.toISOString(),
			};
		}
		if (obj instanceof RegExp) {
			return {
				$type: "RegExp",
				$value: {
					$pattern: obj.source,
					$flags: obj.flags,
				},
			};
		}
		if (obj instanceof Binary) {
			return {
				$type: "Binary",
				$value: Buffer.from(obj.buffer).toString("base64"),
				$subType: obj.sub_type,
			};
		}
		if (obj instanceof Decimal128) {
			return {
				$type: "Decimal128",
				$value: obj.toString(),
			};
		}
		if (Array.isArray(obj)) {
			return [...obj.map(JsonEncoder.encode)];
		}
		if (obj && typeof obj === "object") {
			for (const [key, value] of Object.entries(obj)) {
				obj[key] = JsonEncoder.encode(value);
			}
		}

		return obj;
	}

	// eslint-disable-next-line @typescript-eslint/no-explicit-any
	static decode(obj: any): any {
		if (obj && obj.$type === "ObjectId") {
			return ObjectId.createFromHexString(obj.$value);
		}
		if (obj && obj.$type === "Date") {
			return new Date(obj.$value);
		}
		if (obj && obj.$type === "RegExp") {
			return new RegExp(obj.$value.$pattern, obj.$value.$flags);
		}
		if (obj && obj.$type === "Binary") {
			return new Binary(Buffer.from(obj.$value, "base64"), obj.$subType);
		}
		if (obj && obj.$type === "Decimal128") {
			return Decimal128.fromString(obj.$value);
		}
		if (Array.isArray(obj)) {
			return [...obj.map(JsonEncoder.decode)];
		}
		if (obj && typeof obj === "object") {
			for (const [key, value] of Object.entries(obj)) {
				obj[key] = JsonEncoder.decode(value);
			}
		}

		return obj;
	}
}
