import { assert, unreachable } from "../../shared/src/asserts.js";
import { isPgNumberType, isPgStringType } from "../../zero-cache/src/types/pg-data-type.js";
import { escapePostgresIdentifier } from "@databases/escape-identifier";
import sql, { SQLItemType } from "@databases/sql";
//#region ../z2s/src/sql.ts
function formatPg(sql) {
	const format = new ReusingFormat(escapePostgresIdentifier);
	return sql.format((items) => formatFn(items, format));
}
function formatPgInternalConvert(sql) {
	const format = new SQLConvertFormat(escapePostgresIdentifier);
	return sql.format((items) => formatFn(items, format));
}
var sqlConvert = Symbol("fromJson");
function isSqlConvert(value) {
	return value !== null && typeof value === "object" && sqlConvert in value;
}
function sqlConvertSingularLiteralArg(value) {
	const arg = {
		[sqlConvert]: "literal",
		type: value === null ? "null" : typeof value,
		value,
		plural: false
	};
	return sql.value(arg);
}
function sqlConvertPluralLiteralArg(type, value) {
	const arg = {
		[sqlConvert]: "literal",
		type,
		value,
		plural: true
	};
	return sql.value(arg);
}
function sqlConvertColumnArg(serverColumnSchema, value, plural, isComparison) {
	return sql.value({
		[sqlConvert]: "column",
		type: serverColumnSchema.type,
		isEnum: serverColumnSchema.isEnum,
		value,
		plural: plural || serverColumnSchema.isArray,
		isComparison
	});
}
var ReusingFormat = class {
	#seen = /* @__PURE__ */ new Map();
	escapeIdentifier;
	constructor(escapeIdentifier) {
		this.escapeIdentifier = escapeIdentifier;
	}
	formatValue = (value) => {
		if (this.#seen.has(value)) return {
			placeholder: `$${this.#seen.get(value)}`,
			value: PREVIOUSLY_SEEN_VALUE
		};
		this.#seen.set(value, this.#seen.size + 1);
		return {
			placeholder: `$${this.#seen.size}`,
			value
		};
	};
};
function stringify(arg) {
	if (arg.value === null) return null;
	if (arg.plural) return JSON.stringify(arg.value);
	if (arg[sqlConvert] === "literal" && arg.type === "string") return arg.value;
	if (arg[sqlConvert] === "column" && (arg.isEnum || isPgStringType(arg.type))) return arg.value;
	return JSON.stringify(arg.value);
}
var SQLConvertFormat = class {
	#seen = /* @__PURE__ */ new Map();
	#size = 0;
	escapeIdentifier;
	constructor(escapeIdentifier) {
		this.escapeIdentifier = escapeIdentifier;
	}
	formatValue = (value) => {
		assert(isSqlConvert(value), "JsonPackedFormat can only take JsonPackArgs.");
		const byType = this.#seen.get(value.value);
		if (byType?.has(value.type)) return {
			placeholder: createPlaceholder(byType.get(value.type), value),
			value: PREVIOUSLY_SEEN_VALUE
		};
		this.#size++;
		if (byType) byType.set(value.type, this.#size);
		else this.#seen.set(value.value, new Map([[value.type, this.#size]]));
		return {
			placeholder: createPlaceholder(this.#size, value),
			value: stringify(value)
		};
	};
};
function createPlaceholder(index, arg) {
	if (arg.type === "null") {
		assert(arg.value === null, "Args of type 'null' must have value null");
		assert(!arg.plural, "Args of type 'null' must not be plural");
		return `$${index}`;
	}
	if (arg[sqlConvert] === "literal") {
		const { value } = arg;
		if (Array.isArray(value)) return formatPlural(index, `value::${pgTypeForLiteralType(arg.type)}`);
		return `$${index}::text::${pgTypeForLiteralType(arg.type)}`;
	}
	const common = formatCommonToSingularAndPlural(index, arg);
	return arg.plural ? formatPlural(index, common) : common;
}
function formatCommonToSingularAndPlural(index, arg) {
	const valuePlaceholder = arg.plural ? "value" : `$${index}`;
	let atTimeZone = ` AT TIME ZONE 'UTC'`;
	switch (arg.type) {
		case "timestamptz":
		case "timestamp with time zone": atTimeZone = "";
		case "date":
		case "timestamp":
		case "timestamp without time zone": return `to_timestamp(${valuePlaceholder}::text::numeric / 1000.0)${atTimeZone}`;
		case "timetz":
		case "time with time zone": atTimeZone = "";
		case "time":
		case "time without time zone": return `(${valuePlaceholder}::text::int * interval'1ms')::time${atTimeZone}`;
		case "uuid": return `${valuePlaceholder}::text::uuid`;
	}
	if (arg.isEnum) return `${valuePlaceholder}::text::"${arg.type}"`;
	if (isPgStringType(arg.type)) return arg.isComparison ? `${valuePlaceholder}::text` : `${valuePlaceholder}::text::${arg.type}`;
	if (isPgNumberType(arg.type)) return arg.isComparison ? `${valuePlaceholder}::text::double precision` : `${valuePlaceholder}::text::${arg.type}`;
	return `${valuePlaceholder}::text::${arg.type}`;
}
function formatPlural(index, select) {
	return `ARRAY(
          SELECT ${select} FROM jsonb_array_elements_text($${index}::text::jsonb)
        )`;
}
function pgTypeForLiteralType(type) {
	switch (type) {
		case "boolean": return "boolean";
		case "number": return "double precision";
		case "string": return "text";
		default: unreachable(type);
	}
}
var PREVIOUSLY_SEEN_VALUE = Symbol("PREVIOUSLY_SEEN_VALUE");
var whitespacePrefixRe = /^\s*/;
function formatFn(items, { escapeIdentifier, formatValue }) {
	let text = "";
	const values = [];
	const localIdentifiers = /* @__PURE__ */ new Map();
	for (const item of items) switch (item.type) {
		case SQLItemType.RAW:
			text += item.text;
			break;
		case SQLItemType.VALUE: {
			const { placeholder, value } = formatValue(item.value, values.length);
			text += placeholder;
			if (value !== PREVIOUSLY_SEEN_VALUE) values.push(value);
			break;
		}
		case SQLItemType.IDENTIFIER: {
			const names = item.names.length === 1 && typeof item.names[0] === "string" && item.names[0].includes(".") ? item.names[0].split(".") : item.names;
			text += names.map((name) => {
				if (typeof name === "string") return escapeIdentifier(name);
				if (!localIdentifiers.has(name)) localIdentifiers.set(name, `__local_${localIdentifiers.size}__`);
				return escapeIdentifier(localIdentifiers.get(name));
			}).join(".");
			break;
		}
	}
	if (text.trim()) {
		const lines = text.split("\n");
		const min = Math.min(...lines.filter((l) => l.trim() !== "").map((l) => whitespacePrefixRe.exec(l)[0].length));
		if (min) text = lines.map((line) => line.substr(min)).join("\n");
	}
	return {
		text: text.trim(),
		values
	};
}
//#endregion
export { formatPg, formatPgInternalConvert, sql, sqlConvertColumnArg, sqlConvertPluralLiteralArg, sqlConvertSingularLiteralArg };

//# sourceMappingURL=sql.js.map