import { globalRegistry } from "./registries.js";
function initializeContext(t) {
	let n = t?.target ?? "draft-2020-12";
	return n === "draft-4" && (n = "draft-04"), n === "draft-7" && (n = "draft-07"), {
		processors: t.processors ?? {},
		metadataRegistry: t?.metadata ?? globalRegistry,
		target: n,
		unrepresentable: t?.unrepresentable ?? "throw",
		override: t?.override ?? (() => {}),
		io: t?.io ?? "output",
		counter: 0,
		seen: /* @__PURE__ */ new Map(),
		cycles: t?.cycles ?? "ref",
		reused: t?.reused ?? "inline",
		external: t?.external ?? void 0
	};
}
function process(e, t, r = {
	path: [],
	schemaPath: []
}) {
	var i;
	let o = e._zod.def, s = t.seen.get(e);
	if (s) return s.count++, r.schemaPath.includes(e) && (s.cycle = r.path), s.schema;
	let c = {
		schema: {},
		count: 1,
		cycle: void 0,
		path: r.path
	};
	t.seen.set(e, c);
	let l = e._zod.toJSONSchema?.();
	if (l) c.schema = l;
	else {
		let i = {
			...r,
			schemaPath: [...r.schemaPath, e],
			path: r.path
		};
		if (e._zod.processJSONSchema) e._zod.processJSONSchema(t, c.schema, i);
		else {
			let n = c.schema, r = t.processors[o.type];
			if (!r) throw Error(`[toJSONSchema]: Non-representable type encountered: ${o.type}`);
			r(e, t, n, i);
		}
		let a = e._zod.parent;
		a && (c.ref ||= a, process(a, t, i), t.seen.get(a).isParent = !0);
	}
	let u = t.metadataRegistry.get(e);
	return u && Object.assign(c.schema, u), t.io === "input" && isTransforming(e) && (delete c.schema.examples, delete c.schema.default), t.io === "input" && c.schema._prefault && ((i = c.schema).default ?? (i.default = c.schema._prefault)), delete c.schema._prefault, t.seen.get(e).schema;
}
function extractDefs(e, t) {
	let n = e.seen.get(t);
	if (!n) throw Error("Unprocessed schema. This is a bug in Zod.");
	let r = /* @__PURE__ */ new Map();
	for (let t of e.seen.entries()) {
		let n = e.metadataRegistry.get(t[0])?.id;
		if (n) {
			let e = r.get(n);
			if (e && e !== t[0]) throw Error(`Duplicate schema id "${n}" detected during JSON Schema conversion. Two different schemas cannot share the same id when converted together.`);
			r.set(n, t[0]);
		}
	}
	let i = (t) => {
		let r = e.target === "draft-2020-12" ? "$defs" : "definitions";
		if (e.external) {
			let n = e.external.registry.get(t[0])?.id, i = e.external.uri ?? ((e) => e);
			if (n) return { ref: i(n) };
			let a = t[1].defId ?? t[1].schema.id ?? `schema${e.counter++}`;
			return t[1].defId = a, {
				defId: a,
				ref: `${i("__shared")}#/${r}/${a}`
			};
		}
		if (t[1] === n) return { ref: "#" };
		let i = `#/${r}/`, a = t[1].schema.id ?? `__schema${e.counter++}`;
		return {
			defId: a,
			ref: i + a
		};
	}, a = (e) => {
		if (e[1].schema.$ref) return;
		let t = e[1], { ref: n, defId: r } = i(e);
		t.def = { ...t.schema }, r && (t.defId = r);
		let a = t.schema;
		for (let e in a) delete a[e];
		a.$ref = n;
	};
	if (e.cycles === "throw") for (let t of e.seen.entries()) {
		let e = t[1];
		if (e.cycle) throw Error(`Cycle detected: #/${e.cycle?.join("/")}/<root>

Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);
	}
	for (let n of e.seen.entries()) {
		let r = n[1];
		if (t === n[0]) {
			a(n);
			continue;
		}
		if (e.external) {
			let r = e.external.registry.get(n[0])?.id;
			if (t !== n[0] && r) {
				a(n);
				continue;
			}
		}
		if (e.metadataRegistry.get(n[0])?.id) {
			a(n);
			continue;
		}
		if (r.cycle) {
			a(n);
			continue;
		}
		if (r.count > 1 && e.reused === "ref") {
			a(n);
			continue;
		}
	}
}
function finalize(e, t) {
	let n = e.seen.get(t);
	if (!n) throw Error("Unprocessed schema. This is a bug in Zod.");
	let r = (t) => {
		let n = e.seen.get(t);
		if (n.ref === null) return;
		let i = n.def ?? n.schema, a = { ...i }, o = n.ref;
		if (n.ref = null, o) {
			r(o);
			let n = e.seen.get(o), s = n.schema;
			if (s.$ref && (e.target === "draft-07" || e.target === "draft-04" || e.target === "openapi-3.0") ? (i.allOf = i.allOf ?? [], i.allOf.push(s)) : Object.assign(i, s), Object.assign(i, a), t._zod.parent === o) for (let e in i) e === "$ref" || e === "allOf" || e in a || delete i[e];
			if (s.$ref && n.def) for (let e in i) e === "$ref" || e === "allOf" || e in n.def && JSON.stringify(i[e]) === JSON.stringify(n.def[e]) && delete i[e];
		}
		let s = t._zod.parent;
		if (s && s !== o) {
			r(s);
			let t = e.seen.get(s);
			if (t?.schema.$ref && (i.$ref = t.schema.$ref, t.def)) for (let e in i) e === "$ref" || e === "allOf" || e in t.def && JSON.stringify(i[e]) === JSON.stringify(t.def[e]) && delete i[e];
		}
		e.override({
			zodSchema: t,
			jsonSchema: i,
			path: n.path ?? []
		});
	};
	for (let t of [...e.seen.entries()].reverse()) r(t[0]);
	let i = {};
	if (e.target === "draft-2020-12" ? i.$schema = "https://json-schema.org/draft/2020-12/schema" : e.target === "draft-07" ? i.$schema = "http://json-schema.org/draft-07/schema#" : e.target === "draft-04" ? i.$schema = "http://json-schema.org/draft-04/schema#" : e.target, e.external?.uri) {
		let n = e.external.registry.get(t)?.id;
		if (!n) throw Error("Schema is missing an `id` property");
		i.$id = e.external.uri(n);
	}
	Object.assign(i, n.def ?? n.schema);
	let a = e.external?.defs ?? {};
	for (let t of e.seen.entries()) {
		let e = t[1];
		e.def && e.defId && (a[e.defId] = e.def);
	}
	e.external || Object.keys(a).length > 0 && (e.target === "draft-2020-12" ? i.$defs = a : i.definitions = a);
	try {
		let n = JSON.parse(JSON.stringify(i));
		return Object.defineProperty(n, "~standard", {
			value: {
				...t["~standard"],
				jsonSchema: {
					input: createStandardJSONSchemaMethod(t, "input", e.processors),
					output: createStandardJSONSchemaMethod(t, "output", e.processors)
				}
			},
			enumerable: !1,
			writable: !1
		}), n;
	} catch {
		throw Error("Error converting schema to JSON.");
	}
}
function isTransforming(e, t) {
	let n = t ?? { seen: /* @__PURE__ */ new Set() };
	if (n.seen.has(e)) return !1;
	n.seen.add(e);
	let r = e._zod.def;
	if (r.type === "transform") return !0;
	if (r.type === "array") return isTransforming(r.element, n);
	if (r.type === "set") return isTransforming(r.valueType, n);
	if (r.type === "lazy") return isTransforming(r.getter(), n);
	if (r.type === "promise" || r.type === "optional" || r.type === "nonoptional" || r.type === "nullable" || r.type === "readonly" || r.type === "default" || r.type === "prefault") return isTransforming(r.innerType, n);
	if (r.type === "intersection") return isTransforming(r.left, n) || isTransforming(r.right, n);
	if (r.type === "record" || r.type === "map") return isTransforming(r.keyType, n) || isTransforming(r.valueType, n);
	if (r.type === "pipe") return isTransforming(r.in, n) || isTransforming(r.out, n);
	if (r.type === "object") {
		for (let e in r.shape) if (isTransforming(r.shape[e], n)) return !0;
		return !1;
	}
	if (r.type === "union") {
		for (let e of r.options) if (isTransforming(e, n)) return !0;
		return !1;
	}
	if (r.type === "tuple") {
		for (let e of r.items) if (isTransforming(e, n)) return !0;
		return !!(r.rest && isTransforming(r.rest, n));
	}
	return !1;
}
const createToJSONSchemaMethod = (e, a = {}) => (o) => {
	let s = initializeContext({
		...o,
		processors: a
	});
	return process(e, s), extractDefs(s, e), finalize(s, e);
}, createStandardJSONSchemaMethod = (e, a, o = {}) => (s) => {
	let { libraryOptions: c, target: l } = s ?? {}, u = initializeContext({
		...c ?? {},
		target: l,
		io: a,
		processors: o
	});
	return process(e, u), extractDefs(u, e), finalize(u, e);
};
export { createStandardJSONSchemaMethod, createToJSONSchemaMethod, extractDefs, finalize, initializeContext, process };
