// This file is auto-generated by @hey-api/openapi-ts

interface SerializeOptions<T> extends SerializePrimitiveOptions, SerializerOptions<T> {}

interface SerializePrimitiveOptions {
	allowReserved?: boolean;
	name: string;
}

export interface SerializerOptions<T> {
	/**
	 * @default true
	 */
	explode: boolean;
	style: T;
}

export type ArrayStyle = "form" | "spaceDelimited" | "pipeDelimited";
export type ArraySeparatorStyle = ArrayStyle | MatrixStyle;
type MatrixStyle = "label" | "matrix" | "simple";
export type ObjectStyle = "form" | "deepObject";
type ObjectSeparatorStyle = ObjectStyle | MatrixStyle;

interface SerializePrimitiveParam extends SerializePrimitiveOptions {
	value: string;
}

export const separatorArrayExplode = (style: ArraySeparatorStyle) => {
	switch (style) {
		case "label":
			return ".";
		case "matrix":
			return ";";
		case "simple":
			return ",";
		default:
			return "&";
	}
};

export const separatorArrayNoExplode = (style: ArraySeparatorStyle) => {
	switch (style) {
		case "form":
			return ",";
		case "pipeDelimited":
			return "|";
		case "spaceDelimited":
			return "%20";
		default:
			return ",";
	}
};

export const separatorObjectExplode = (style: ObjectSeparatorStyle) => {
	switch (style) {
		case "label":
			return ".";
		case "matrix":
			return ";";
		case "simple":
			return ",";
		default:
			return "&";
	}
};

export const serializeArrayParam = ({
	allowReserved,
	explode,
	name,
	style,
	value,
}: SerializeOptions<ArraySeparatorStyle> & {
	value: unknown[];
}) => {
	if (!explode) {
		const joinedValues = (allowReserved ? value : value.map((v) => encodeURIComponent(v as string))).join(
			separatorArrayNoExplode(style),
		);
		switch (style) {
			case "label":
				return `.${joinedValues}`;
			case "matrix":
				return `;${name}=${joinedValues}`;
			case "simple":
				return joinedValues;
			default:
				return `${name}=${joinedValues}`;
		}
	}

	const separator = separatorArrayExplode(style);
	const joinedValues = value
		.map((v) => {
			if (style === "label" || style === "simple") {
				return allowReserved ? v : encodeURIComponent(v as string);
			}

			return serializePrimitiveParam({
				allowReserved,
				name,
				value: v as string,
			});
		})
		.join(separator);
	return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
};

export const serializePrimitiveParam = ({ allowReserved, name, value }: SerializePrimitiveParam) => {
	if (value === undefined || value === null) {
		return "";
	}

	if (typeof value === "object") {
		throw new Error(
			"Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.",
		);
	}

	return `${name}=${allowReserved ? value : encodeURIComponent(value)}`;
};

export const serializeObjectParam = ({
	allowReserved,
	explode,
	name,
	style,
	value,
	valueOnly,
}: SerializeOptions<ObjectSeparatorStyle> & {
	value: Record<string, unknown> | Date;
	valueOnly?: boolean;
}) => {
	if (value instanceof Date) {
		return valueOnly ? value.toISOString() : `${name}=${value.toISOString()}`;
	}

	if (style !== "deepObject" && !explode) {
		let values: string[] = [];
		Object.entries(value).forEach(([key, v]) => {
			values = [...values, key, allowReserved ? (v as string) : encodeURIComponent(v as string)];
		});
		const joinedValues = values.join(",");
		switch (style) {
			case "form":
				return `${name}=${joinedValues}`;
			case "label":
				return `.${joinedValues}`;
			case "matrix":
				return `;${name}=${joinedValues}`;
			default:
				return joinedValues;
		}
	}

	const separator = separatorObjectExplode(style);
	const joinedValues = Object.entries(value)
		.map(([key, v]) =>
			serializePrimitiveParam({
				allowReserved,
				name: style === "deepObject" ? `${name}[${key}]` : key,
				value: v as string,
			}),
		)
		.join(separator);
	return style === "label" || style === "matrix" ? separator + joinedValues : joinedValues;
};
