import { $ZodRegistry as j, globalRegistry as T } from "./registries.js";
import { getEnumValues as N } from "./util.js";
class x {
  constructor(a) {
    this.counter = 0, this.metadataRegistry = a?.metadata ?? T, this.target = a?.target ?? "draft-2020-12", this.unrepresentable = a?.unrepresentable ?? "throw", this.override = a?.override ?? (() => {
    }), this.io = a?.io ?? "output", this.seen = /* @__PURE__ */ new Map();
  }
  process(a, c = { path: [], schemaPath: [] }) {
    var h;
    const t = a._zod.def, b = {
      guid: "uuid",
      url: "uri",
      datetime: "date-time",
      json_string: "json-string",
      regex: ""
      // do not set
    }, w = this.seen.get(a);
    if (w)
      return w.count++, c.schemaPath.includes(a) && (w.cycle = c.path), w.schema;
    const f = { schema: {}, count: 1, cycle: void 0, path: c.path };
    this.seen.set(a, f);
    const y = a._zod.toJSONSchema?.();
    if (y)
      f.schema = y;
    else {
      const r = {
        ...c,
        schemaPath: [...c.schemaPath, a],
        path: c.path
      }, d = a._zod.parent;
      if (d)
        f.ref = d, this.process(d, r), this.seen.get(d).isParent = !0;
      else {
        const i = f.schema;
        switch (t.type) {
          case "string": {
            const e = i;
            e.type = "string";
            const { minimum: n, maximum: s, format: p, patterns: l, contentEncoding: u } = a._zod.bag;
            if (typeof n == "number" && (e.minLength = n), typeof s == "number" && (e.maxLength = s), p && (e.format = b[p] ?? p, e.format === "" && delete e.format), u && (e.contentEncoding = u), l && l.size > 0) {
              const g = [...l];
              g.length === 1 ? e.pattern = g[0].source : g.length > 1 && (f.schema.allOf = [
                ...g.map((S) => ({
                  ...this.target === "draft-7" || this.target === "draft-4" || this.target === "openapi-3.0" ? { type: "string" } : {},
                  pattern: S.source
                }))
              ]);
            }
            break;
          }
          case "number": {
            const e = i, { minimum: n, maximum: s, format: p, multipleOf: l, exclusiveMaximum: u, exclusiveMinimum: g } = a._zod.bag;
            typeof p == "string" && p.includes("int") ? e.type = "integer" : e.type = "number", typeof g == "number" && (this.target === "draft-4" || this.target === "openapi-3.0" ? (e.minimum = g, e.exclusiveMinimum = !0) : e.exclusiveMinimum = g), typeof n == "number" && (e.minimum = n, typeof g == "number" && this.target !== "draft-4" && (g >= n ? delete e.minimum : delete e.exclusiveMinimum)), typeof u == "number" && (this.target === "draft-4" || this.target === "openapi-3.0" ? (e.maximum = u, e.exclusiveMaximum = !0) : e.exclusiveMaximum = u), typeof s == "number" && (e.maximum = s, typeof u == "number" && this.target !== "draft-4" && (u <= s ? delete e.maximum : delete e.exclusiveMaximum)), typeof l == "number" && (e.multipleOf = l);
            break;
          }
          case "boolean": {
            const e = i;
            e.type = "boolean";
            break;
          }
          case "bigint": {
            if (this.unrepresentable === "throw")
              throw new Error("BigInt cannot be represented in JSON Schema");
            break;
          }
          case "symbol": {
            if (this.unrepresentable === "throw")
              throw new Error("Symbols cannot be represented in JSON Schema");
            break;
          }
          case "null": {
            this.target === "openapi-3.0" ? (i.type = "string", i.nullable = !0, i.enum = [null]) : i.type = "null";
            break;
          }
          case "any":
            break;
          case "unknown":
            break;
          case "undefined": {
            if (this.unrepresentable === "throw")
              throw new Error("Undefined cannot be represented in JSON Schema");
            break;
          }
          case "void": {
            if (this.unrepresentable === "throw")
              throw new Error("Void cannot be represented in JSON Schema");
            break;
          }
          case "never": {
            i.not = {};
            break;
          }
          case "date": {
            if (this.unrepresentable === "throw")
              throw new Error("Date cannot be represented in JSON Schema");
            break;
          }
          case "array": {
            const e = i, { minimum: n, maximum: s } = a._zod.bag;
            typeof n == "number" && (e.minItems = n), typeof s == "number" && (e.maxItems = s), e.type = "array", e.items = this.process(t.element, { ...r, path: [...r.path, "items"] });
            break;
          }
          case "object": {
            const e = i;
            e.type = "object", e.properties = {};
            const n = t.shape;
            for (const l in n)
              e.properties[l] = this.process(n[l], {
                ...r,
                path: [...r.path, "properties", l]
              });
            const s = new Set(Object.keys(n)), p = new Set([...s].filter((l) => {
              const u = t.shape[l]._zod;
              return this.io === "input" ? u.optin === void 0 : u.optout === void 0;
            }));
            p.size > 0 && (e.required = Array.from(p)), t.catchall?._zod.def.type === "never" ? e.additionalProperties = !1 : t.catchall ? t.catchall && (e.additionalProperties = this.process(t.catchall, {
              ...r,
              path: [...r.path, "additionalProperties"]
            })) : this.io === "output" && (e.additionalProperties = !1);
            break;
          }
          case "union": {
            const e = i, n = t.options.map((s, p) => this.process(s, {
              ...r,
              path: [...r.path, "anyOf", p]
            }));
            e.anyOf = n;
            break;
          }
          case "intersection": {
            const e = i, n = this.process(t.left, {
              ...r,
              path: [...r.path, "allOf", 0]
            }), s = this.process(t.right, {
              ...r,
              path: [...r.path, "allOf", 1]
            }), p = (u) => "allOf" in u && Object.keys(u).length === 1, l = [
              ...p(n) ? n.allOf : [n],
              ...p(s) ? s.allOf : [s]
            ];
            e.allOf = l;
            break;
          }
          case "tuple": {
            const e = i;
            e.type = "array";
            const n = this.target === "draft-2020-12" ? "prefixItems" : "items", s = this.target === "draft-2020-12" || this.target === "openapi-3.0" ? "items" : "additionalItems", p = t.items.map((S, v) => this.process(S, {
              ...r,
              path: [...r.path, n, v]
            })), l = t.rest ? this.process(t.rest, {
              ...r,
              path: [...r.path, s, ...this.target === "openapi-3.0" ? [t.items.length] : []]
            }) : null;
            this.target === "draft-2020-12" ? (e.prefixItems = p, l && (e.items = l)) : this.target === "openapi-3.0" ? (e.items = {
              anyOf: p
            }, l && e.items.anyOf.push(l), e.minItems = p.length, l || (e.maxItems = p.length)) : (e.items = p, l && (e.additionalItems = l));
            const { minimum: u, maximum: g } = a._zod.bag;
            typeof u == "number" && (e.minItems = u), typeof g == "number" && (e.maxItems = g);
            break;
          }
          case "record": {
            const e = i;
            e.type = "object", (this.target === "draft-7" || this.target === "draft-2020-12") && (e.propertyNames = this.process(t.keyType, {
              ...r,
              path: [...r.path, "propertyNames"]
            })), e.additionalProperties = this.process(t.valueType, {
              ...r,
              path: [...r.path, "additionalProperties"]
            });
            break;
          }
          case "map": {
            if (this.unrepresentable === "throw")
              throw new Error("Map cannot be represented in JSON Schema");
            break;
          }
          case "set": {
            if (this.unrepresentable === "throw")
              throw new Error("Set cannot be represented in JSON Schema");
            break;
          }
          case "enum": {
            const e = i, n = N(t.entries);
            n.every((s) => typeof s == "number") && (e.type = "number"), n.every((s) => typeof s == "string") && (e.type = "string"), e.enum = n;
            break;
          }
          case "literal": {
            const e = i, n = [];
            for (const s of t.values)
              if (s === void 0) {
                if (this.unrepresentable === "throw")
                  throw new Error("Literal `undefined` cannot be represented in JSON Schema");
              } else if (typeof s == "bigint") {
                if (this.unrepresentable === "throw")
                  throw new Error("BigInt literals cannot be represented in JSON Schema");
                n.push(Number(s));
              } else
                n.push(s);
            if (n.length !== 0) if (n.length === 1) {
              const s = n[0];
              e.type = s === null ? "null" : typeof s, this.target === "draft-4" || this.target === "openapi-3.0" ? e.enum = [s] : e.const = s;
            } else
              n.every((s) => typeof s == "number") && (e.type = "number"), n.every((s) => typeof s == "string") && (e.type = "string"), n.every((s) => typeof s == "boolean") && (e.type = "string"), n.every((s) => s === null) && (e.type = "null"), e.enum = n;
            break;
          }
          case "file": {
            const e = i, n = {
              type: "string",
              format: "binary",
              contentEncoding: "binary"
            }, { minimum: s, maximum: p, mime: l } = a._zod.bag;
            s !== void 0 && (n.minLength = s), p !== void 0 && (n.maxLength = p), l ? l.length === 1 ? (n.contentMediaType = l[0], Object.assign(e, n)) : e.anyOf = l.map((u) => ({ ...n, contentMediaType: u })) : Object.assign(e, n);
            break;
          }
          case "transform": {
            if (this.unrepresentable === "throw")
              throw new Error("Transforms cannot be represented in JSON Schema");
            break;
          }
          case "nullable": {
            const e = this.process(t.innerType, r);
            this.target === "openapi-3.0" ? (f.ref = t.innerType, i.nullable = !0) : i.anyOf = [e, { type: "null" }];
            break;
          }
          case "nonoptional": {
            this.process(t.innerType, r), f.ref = t.innerType;
            break;
          }
          case "success": {
            const e = i;
            e.type = "boolean";
            break;
          }
          case "default": {
            this.process(t.innerType, r), f.ref = t.innerType, i.default = JSON.parse(JSON.stringify(t.defaultValue));
            break;
          }
          case "prefault": {
            this.process(t.innerType, r), f.ref = t.innerType, this.io === "input" && (i._prefault = JSON.parse(JSON.stringify(t.defaultValue)));
            break;
          }
          case "catch": {
            this.process(t.innerType, r), f.ref = t.innerType;
            let e;
            try {
              e = t.catchValue(void 0);
            } catch {
              throw new Error("Dynamic catch values are not supported in JSON Schema");
            }
            i.default = e;
            break;
          }
          case "nan": {
            if (this.unrepresentable === "throw")
              throw new Error("NaN cannot be represented in JSON Schema");
            break;
          }
          case "template_literal": {
            const e = i, n = a._zod.pattern;
            if (!n)
              throw new Error("Pattern not found in template literal");
            e.type = "string", e.pattern = n.source;
            break;
          }
          case "pipe": {
            const e = this.io === "input" ? t.in._zod.def.type === "transform" ? t.out : t.in : t.out;
            this.process(e, r), f.ref = e;
            break;
          }
          case "readonly": {
            this.process(t.innerType, r), f.ref = t.innerType, i.readOnly = !0;
            break;
          }
          // passthrough types
          case "promise": {
            this.process(t.innerType, r), f.ref = t.innerType;
            break;
          }
          case "optional": {
            this.process(t.innerType, r), f.ref = t.innerType;
            break;
          }
          case "lazy": {
            const e = a._zod.innerType;
            this.process(e, r), f.ref = e;
            break;
          }
          case "custom": {
            if (this.unrepresentable === "throw")
              throw new Error("Custom types cannot be represented in JSON Schema");
            break;
          }
          case "function": {
            if (this.unrepresentable === "throw")
              throw new Error("Function types cannot be represented in JSON Schema");
            break;
          }
        }
      }
    }
    const O = this.metadataRegistry.get(a);
    return O && Object.assign(f.schema, O), this.io === "input" && m(a) && (delete f.schema.examples, delete f.schema.default), this.io === "input" && f.schema._prefault && ((h = f.schema).default ?? (h.default = f.schema._prefault)), delete f.schema._prefault, this.seen.get(a).schema;
  }
  emit(a, c) {
    const h = {
      cycles: c?.cycles ?? "ref",
      reused: c?.reused ?? "inline",
      // unrepresentable: _params?.unrepresentable ?? "throw",
      // uri: _params?.uri ?? ((id) => `${id}`),
      external: c?.external ?? void 0
    }, t = this.seen.get(a);
    if (!t)
      throw new Error("Unprocessed schema. This is a bug in Zod.");
    const b = (o) => {
      const r = this.target === "draft-2020-12" ? "$defs" : "definitions";
      if (h.external) {
        const n = h.external.registry.get(o[0])?.id, s = h.external.uri ?? ((l) => l);
        if (n)
          return { ref: s(n) };
        const p = o[1].defId ?? o[1].schema.id ?? `schema${this.counter++}`;
        return o[1].defId = p, { defId: p, ref: `${s("__shared")}#/${r}/${p}` };
      }
      if (o[1] === t)
        return { ref: "#" };
      const i = `#/${r}/`, e = o[1].schema.id ?? `__schema${this.counter++}`;
      return { defId: e, ref: i + e };
    }, w = (o) => {
      if (o[1].schema.$ref)
        return;
      const r = o[1], { ref: d, defId: i } = b(o);
      r.def = { ...r.schema }, i && (r.defId = i);
      const e = r.schema;
      for (const n in e)
        delete e[n];
      e.$ref = d;
    };
    if (h.cycles === "throw")
      for (const o of this.seen.entries()) {
        const r = o[1];
        if (r.cycle)
          throw new Error(`Cycle detected: #/${r.cycle?.join("/")}/<root>

Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.`);
      }
    for (const o of this.seen.entries()) {
      const r = o[1];
      if (a === o[0]) {
        w(o);
        continue;
      }
      if (h.external) {
        const i = h.external.registry.get(o[0])?.id;
        if (a !== o[0] && i) {
          w(o);
          continue;
        }
      }
      if (this.metadataRegistry.get(o[0])?.id) {
        w(o);
        continue;
      }
      if (r.cycle) {
        w(o);
        continue;
      }
      if (r.count > 1 && h.reused === "ref") {
        w(o);
        continue;
      }
    }
    const f = (o, r) => {
      const d = this.seen.get(o), i = d.def ?? d.schema, e = { ...i };
      if (d.ref === null)
        return;
      const n = d.ref;
      if (d.ref = null, n) {
        f(n, r);
        const s = this.seen.get(n).schema;
        s.$ref && (r.target === "draft-7" || r.target === "draft-4" || r.target === "openapi-3.0") ? (i.allOf = i.allOf ?? [], i.allOf.push(s)) : (Object.assign(i, s), Object.assign(i, e));
      }
      d.isParent || this.override({
        zodSchema: o,
        jsonSchema: i,
        path: d.path ?? []
      });
    };
    for (const o of [...this.seen.entries()].reverse())
      f(o[0], { target: this.target });
    const y = {};
    if (this.target === "draft-2020-12" ? y.$schema = "https://json-schema.org/draft/2020-12/schema" : this.target === "draft-7" ? y.$schema = "http://json-schema.org/draft-07/schema#" : this.target === "draft-4" ? y.$schema = "http://json-schema.org/draft-04/schema#" : this.target === "openapi-3.0" || console.warn(`Invalid target: ${this.target}`), h.external?.uri) {
      const o = h.external.registry.get(a)?.id;
      if (!o)
        throw new Error("Schema is missing an `id` property");
      y.$id = h.external.uri(o);
    }
    Object.assign(y, t.def);
    const O = h.external?.defs ?? {};
    for (const o of this.seen.entries()) {
      const r = o[1];
      r.def && r.defId && (O[r.defId] = r.def);
    }
    h.external || Object.keys(O).length > 0 && (this.target === "draft-2020-12" ? y.$defs = O : y.definitions = O);
    try {
      return JSON.parse(JSON.stringify(y));
    } catch {
      throw new Error("Error converting schema to JSON.");
    }
  }
}
function J(k, a) {
  if (k instanceof j) {
    const h = new x(a), t = {};
    for (const f of k._idmap.entries()) {
      const [y, O] = f;
      h.process(O);
    }
    const b = {}, w = {
      registry: k,
      uri: a?.uri,
      defs: t
    };
    for (const f of k._idmap.entries()) {
      const [y, O] = f;
      b[y] = h.emit(O, {
        ...a,
        external: w
      });
    }
    if (Object.keys(t).length > 0) {
      const f = h.target === "draft-2020-12" ? "$defs" : "definitions";
      b.__shared = {
        [f]: t
      };
    }
    return { schemas: b };
  }
  const c = new x(a);
  return c.process(k), c.emit(k, a);
}
function m(k, a) {
  const c = a ?? { seen: /* @__PURE__ */ new Set() };
  if (c.seen.has(k))
    return !1;
  c.seen.add(k);
  const t = k._zod.def;
  switch (t.type) {
    case "string":
    case "number":
    case "bigint":
    case "boolean":
    case "date":
    case "symbol":
    case "undefined":
    case "null":
    case "any":
    case "unknown":
    case "never":
    case "void":
    case "literal":
    case "enum":
    case "nan":
    case "file":
    case "template_literal":
      return !1;
    case "array":
      return m(t.element, c);
    case "object": {
      for (const b in t.shape)
        if (m(t.shape[b], c))
          return !0;
      return !1;
    }
    case "union": {
      for (const b of t.options)
        if (m(b, c))
          return !0;
      return !1;
    }
    case "intersection":
      return m(t.left, c) || m(t.right, c);
    case "tuple": {
      for (const b of t.items)
        if (m(b, c))
          return !0;
      return !!(t.rest && m(t.rest, c));
    }
    case "record":
      return m(t.keyType, c) || m(t.valueType, c);
    case "map":
      return m(t.keyType, c) || m(t.valueType, c);
    case "set":
      return m(t.valueType, c);
    // inner types
    case "promise":
    case "optional":
    case "nonoptional":
    case "nullable":
    case "readonly":
      return m(t.innerType, c);
    case "lazy":
      return m(t.getter(), c);
    case "default":
      return m(t.innerType, c);
    case "prefault":
      return m(t.innerType, c);
    case "custom":
      return !1;
    case "transform":
      return !0;
    case "pipe":
      return m(t.in, c) || m(t.out, c);
    case "success":
      return !1;
    case "catch":
      return !1;
    case "function":
      return !1;
  }
  throw new Error(`Unknown schema type: ${t.type}`);
}
export {
  x as JSONSchemaGenerator,
  J as toJSONSchema
};
//# sourceMappingURL=to-json-schema.js.map
