import { z } from "zod";
const HTTP_METHODS = ["GET", "POST", "PUT", "PATCH", "DELETE"];
function resolveType(def) {
  if (!def) return void 0;
  if (typeof def.typeName === "string" && def.typeName.length) return def.typeName;
  if (typeof def.type === "string" && def.type.length) return def.type;
  return void 0;
}
function getShape(def) {
  if (!def) return {};
  const shape = typeof def.shape === "function" ? def.shape() : def.shape;
  if (shape && typeof shape === "object") return shape;
  return {};
}
function normalizeChecks(checks) {
  if (!Array.isArray(checks)) return [];
  return checks.map((check) => {
    if (!check) return {};
    const base = check?._zod?.def ?? check?.def ?? check;
    const kind = typeof check?.kind === "string" ? check.kind : typeof base?.check === "string" ? base.check : void 0;
    const value = base?.value ?? base?.minimum ?? base?.maximum ?? base?.exact ?? base?.length ?? base?.limit ?? base?.includes ?? base?.min ?? base?.max;
    return { kind, value, extra: base && typeof base === "object" ? base : void 0 };
  });
}
const DEFAULT_EXAMPLE_VALUES = {
  string: "string",
  number: 1,
  integer: 1,
  boolean: true,
  uuid: "00000000-0000-4000-8000-000000000000",
  email: "user@example.com",
  url: "https://example.com/resource",
  datetime: (/* @__PURE__ */ new Date("2025-01-01T00:00:00.000Z")).toISOString()
};
function toTitle(str) {
  if (!str) return "";
  return str.split(/[_\-\s]+/).map((part) => part.charAt(0).toUpperCase() + part.slice(1)).join(" ");
}
function normalizePath(path) {
  const segments = path.split("/").filter((seg) => seg.length > 0);
  const params = [];
  const normalized = segments.map((seg) => {
    const catchAll = seg.match(/^\[\.\.\.(.+)\]$/);
    if (catchAll) {
      params.push({ name: catchAll[1], catchAll: true });
      return `{${catchAll[1]}}`;
    }
    const optCatchAll = seg.match(/^\[\[\.\.\.(.+)\]\]$/);
    if (optCatchAll) {
      params.push({ name: optCatchAll[1], catchAll: true, optional: true });
      return `{${optCatchAll[1]}}`;
    }
    const dyn = seg.match(/^\[(.+)\]$/);
    if (dyn) {
      params.push({ name: dyn[1] });
      return `{${dyn[1]}}`;
    }
    return seg;
  }).join("/");
  return { path: "/" + normalized, params };
}
function unwrap(schema) {
  if (!schema) {
    return { schema: void 0, optional: true, nullable: false };
  }
  let current = schema;
  let optional = false;
  let nullable = false;
  let defaultValue;
  while (true) {
    const def = current?._def;
    if (!def) {
      return { schema: current, optional, nullable, defaultValue };
    }
    const typeName = resolveType(def);
    if (typeName === "ZodOptional" || typeName === "optional") {
      optional = true;
      current = current._def.innerType;
      continue;
    }
    if (typeName === "ZodNullable" || typeName === "nullable") {
      nullable = true;
      current = current._def.innerType;
      continue;
    }
    if (typeName === "ZodDefault" || typeName === "default") {
      optional = true;
      const rawDefault = current._def.defaultValue;
      defaultValue = typeof rawDefault === "function" ? rawDefault() : rawDefault;
      current = current._def.innerType;
      continue;
    }
    if (typeName === "ZodPipeline" || typeName === "pipe") {
      current = current._def.out ?? current._def.innerType ?? current._def.schema;
      continue;
    }
    if (typeName === "transformer") {
      current = current._def.output;
      continue;
    }
    if (typeName === "ZodLazy" || typeName === "lazy") {
      const getter = current._def.getter;
      current = typeof getter === "function" ? getter() : current;
      if (current === schema) break;
      continue;
    }
    if (typeName === "ZodPromise" || typeName === "promise") {
      current = current._def.type;
      continue;
    }
    if (typeName === "ZodCatch" || typeName === "catch") {
      current = current._def.innerType;
      continue;
    }
    if (typeName === "ZodReadonly" || typeName === "readonly") {
      current = current._def.innerType;
      continue;
    }
    if (typeName === "ZodBranded" || typeName === "branded") {
      current = current._def.type;
      continue;
    }
    break;
  }
  return { schema: current, optional, nullable, defaultValue };
}
function extractZodDescription(schema) {
  if (!schema) return void 0;
  let current = schema;
  while (current) {
    const directDescription = current.description;
    if (typeof directDescription === "string" && directDescription.length > 0) {
      return directDescription;
    }
    const def = current?._def;
    if (!def) return void 0;
    if (typeof def.description === "string" && def.description.length > 0) {
      return def.description;
    }
    const typeName = resolveType(def);
    if (typeName === "ZodOptional" || typeName === "optional" || typeName === "ZodNullable" || typeName === "nullable" || typeName === "ZodDefault" || typeName === "default" || typeName === "ZodCatch" || typeName === "catch" || typeName === "ZodReadonly" || typeName === "readonly") {
      current = def.innerType;
      continue;
    }
    if (typeName === "ZodBranded" || typeName === "branded") {
      current = def.type;
      continue;
    }
    if (typeName === "ZodPipeline" || typeName === "pipe") {
      current = def.out ?? def.innerType ?? def.schema;
      continue;
    }
    if (typeName === "transformer") {
      current = def.output;
      continue;
    }
    if (typeName === "ZodLazy" || typeName === "lazy") {
      const getter = def.getter;
      const next = typeof getter === "function" ? getter() : current;
      if (next === current) break;
      current = next;
      continue;
    }
    if (typeName === "ZodPromise" || typeName === "promise") {
      current = def.type;
      continue;
    }
    break;
  }
  return void 0;
}
function zodToJsonSchema(schema, ctx) {
  if (!schema) return void 0;
  const context = ctx ?? { memo: /* @__PURE__ */ new WeakMap() };
  const cached = context.memo.get(schema);
  if (cached) return cached;
  const placeholder = {};
  context.memo.set(schema, placeholder);
  const { schema: inner, nullable } = unwrap(schema);
  if (!inner) {
    if (nullable) placeholder.nullable = true;
    return placeholder;
  }
  if (inner !== schema && typeof inner === "object") {
    if (!context.memo.has(inner)) {
      context.memo.set(inner, placeholder);
    }
  }
  const def = inner._def;
  if (!def) return placeholder;
  const typeName = resolveType(def);
  const result = placeholder;
  switch (typeName) {
    case "ZodString":
    case "string": {
      result.type = "string";
      const checks = normalizeChecks(def.checks);
      for (const check of checks) {
        if (!check.kind) continue;
        if (check.kind === "uuid" || check.extra?.format === "uuid") {
          result.format = "uuid";
        } else if (check.kind === "email" || check.extra?.format === "email") {
          result.format = "email";
        } else if (check.kind === "url" || check.extra?.format === "uri" || check.extra?.format === "url") {
          result.format = "uri";
        } else if (check.kind === "regex" && check.extra?.pattern instanceof RegExp) {
          result.pattern = check.extra.pattern.source;
        } else if (check.kind === "string_format" && typeof check.extra?.format === "string") {
          if (check.extra.format === "regex" && check.extra.pattern != null) {
            result.pattern = check.extra.pattern instanceof RegExp ? check.extra.pattern.source : String(check.extra.pattern);
          } else {
            result.format = check.extra.format;
          }
        } else if (check.kind === "datetime" || check.extra?.format === "date-time") {
          result.format = "date-time";
        } else if (["length", "len", "exact_length"].includes(check.kind ?? "")) {
          if (typeof check.value === "number") {
            result.minLength = check.value;
            result.maxLength = check.value;
          }
        } else if (check.kind === "min" || check.kind === "min_length") {
          if (typeof check.value === "number") result.minLength = check.value;
        } else if (check.kind === "max" || check.kind === "max_length") {
          if (typeof check.value === "number") result.maxLength = check.value;
        }
      }
      break;
    }
    case "ZodNumber":
    case "number": {
      result.type = "number";
      const checks = normalizeChecks(def.checks);
      for (const check of checks) {
        if (!check.kind) continue;
        if (check.kind === "int" || check.kind === "isInteger") result.type = "integer";
        if ((check.kind === "min" || check.kind === "gte") && typeof check.value === "number") result.minimum = check.value;
        if ((check.kind === "max" || check.kind === "lte") && typeof check.value === "number") result.maximum = check.value;
        if (check.kind === "multipleOf" && typeof check.value === "number") result.multipleOf = check.value;
      }
      break;
    }
    case "ZodBigInt":
    case "bigint":
      result.type = "integer";
      result.format = "int64";
      break;
    case "ZodBoolean":
    case "boolean":
      result.type = "boolean";
      break;
    case "ZodLiteral":
    case "literal": {
      const value = def.value ?? (Array.isArray(def.values) ? def.values[0] : void 0);
      result.type = typeof value;
      result.enum = [value];
      break;
    }
    case "ZodEnum":
    case "enum": {
      const entries = def.values ?? def.entries;
      const values = Array.isArray(entries) ? entries : entries ? Object.values(entries) : [];
      const enumerators = values.filter((v) => typeof v === "string" || typeof v === "number");
      const allString = enumerators.every((v) => typeof v === "string");
      result.type = allString ? "string" : "number";
      result.enum = enumerators;
      break;
    }
    case "ZodNativeEnum": {
      const values = Object.values(def.values).filter((v) => typeof v === "string" || typeof v === "number");
      const allString = values.every((v) => typeof v === "string");
      result.type = allString ? "string" : "number";
      result.enum = values;
      break;
    }
    case "ZodUnion":
    case "union": {
      const options = def.options || [];
      result.oneOf = options.map((option) => zodToJsonSchema(option, context) ?? {});
      break;
    }
    case "ZodIntersection":
    case "intersection": {
      result.allOf = [
        zodToJsonSchema(def.left, context) ?? {},
        zodToJsonSchema(def.right, context) ?? {}
      ];
      break;
    }
    case "ZodPipeline":
    case "pipe": {
      const resolved = zodToJsonSchema(def.out ?? def.innerType ?? def.schema, context) ?? {};
      Object.assign(result, resolved);
      break;
    }
    case "ZodLazy":
    case "lazy": {
      const next = typeof def.getter === "function" ? def.getter() : void 0;
      const resolved = next ? zodToJsonSchema(next, context) : void 0;
      if (resolved) Object.assign(result, resolved);
      break;
    }
    case "ZodPromise":
    case "promise": {
      const resolved = zodToJsonSchema(def.type, context);
      if (resolved) Object.assign(result, resolved);
      break;
    }
    case "ZodCatch":
    case "catch": {
      const resolved = zodToJsonSchema(def.innerType ?? def.type, context);
      if (resolved) Object.assign(result, resolved);
      break;
    }
    case "ZodReadonly":
    case "readonly": {
      const resolved = zodToJsonSchema(def.innerType ?? def.type, context);
      if (resolved) Object.assign(result, resolved);
      break;
    }
    case "ZodArray":
    case "array": {
      const elementSchema = def.type && typeof def.type === "object" ? def.type : def.element && typeof def.element === "object" ? def.element : void 0;
      result.type = "array";
      result.items = zodToJsonSchema(elementSchema, context) ?? {};
      if (typeof def.minLength === "number") result.minItems = def.minLength;
      if (typeof def.maxLength === "number") result.maxItems = def.maxLength;
      const checks = normalizeChecks(def.checks);
      for (const check of checks) {
        if (check.kind === "min_length" && typeof check.value === "number") result.minItems = check.value;
        if (check.kind === "max_length" && typeof check.value === "number") result.maxItems = check.value;
        if (check.kind === "length" && typeof check.value === "number") {
          result.minItems = check.value;
          result.maxItems = check.value;
        }
      }
      break;
    }
    case "ZodTuple":
    case "tuple": {
      const items = def.items || [];
      result.type = "array";
      result.prefixItems = items.map((item) => zodToJsonSchema(item, context) ?? {});
      result.minItems = items.length;
      result.maxItems = items.length;
      break;
    }
    case "ZodRecord":
    case "record": {
      result.type = "object";
      const valueSchema = def.valueType ?? def.value;
      const valueType = valueSchema ? resolveType(valueSchema._def) : void 0;
      if (!valueType || valueType === "ZodUnknown" || valueType === "unknown" || valueType === "ZodAny" || valueType === "any") {
        result.additionalProperties = true;
      } else {
        result.additionalProperties = zodToJsonSchema(valueSchema, context) ?? true;
      }
      break;
    }
    case "ZodObject":
    case "object": {
      result.type = "object";
      const shape = getShape(def);
      const properties = {};
      const required = [];
      for (const [key, rawSchema] of Object.entries(shape)) {
        const unwrapped = unwrap(rawSchema);
        const childSchema = zodToJsonSchema(unwrapped.schema, context);
        if (!childSchema) continue;
        const baseSchema = childSchema;
        let propertySchema = baseSchema;
        if (unwrapped.nullable) {
          propertySchema = {
            anyOf: [{ type: "null" }, propertySchema]
          };
        }
        if (unwrapped.defaultValue !== void 0) {
          if (propertySchema === baseSchema) {
            propertySchema = { allOf: [baseSchema], default: unwrapped.defaultValue };
          } else {
            propertySchema = { ...propertySchema, default: unwrapped.defaultValue };
          }
        }
        properties[key] = propertySchema;
        if (!unwrapped.optional) required.push(key);
      }
      result.properties = properties;
      if (required.length > 0) result.required = required;
      if (def.unknownKeys === "passthrough") {
        result.additionalProperties = true;
      } else if (def.catchall) {
        const catchallType = resolveType(def.catchall._def);
        if (catchallType === "ZodNever" || catchallType === "never") {
          result.additionalProperties = false;
        } else if (!catchallType || catchallType === "ZodUnknown" || catchallType === "unknown" || catchallType === "ZodAny" || catchallType === "any") {
          result.additionalProperties = true;
        } else {
          result.additionalProperties = zodToJsonSchema(def.catchall, context) ?? true;
        }
      } else {
        result.additionalProperties = false;
      }
      break;
    }
    case "ZodDate":
    case "date":
      result.type = "string";
      result.format = "date-time";
      break;
    case "ZodNull":
    case "null":
      result.type = "null";
      break;
    case "ZodVoid":
    case "void":
    case "ZodNever":
    case "never":
      break;
    case "ZodAny":
    case "any":
    case "ZodUnknown":
    case "unknown":
    case "ZodNaN":
    case "nan":
    default:
      break;
  }
  if (nullable) {
    if (result.type && result.type !== "null") {
      result.nullable = true;
    } else if (!result.type) {
      const clone = { ...result };
      result.anyOf = [{ type: "null" }, clone];
    }
  }
  return result;
}
function generateExample(schema, ctx) {
  if (!schema) return void 0;
  if ((typeof schema !== "object" || schema === null) && typeof schema !== "function") return void 0;
  const trackable = schema;
  const context = ctx ?? { stack: /* @__PURE__ */ new WeakSet() };
  if (context.stack.has(trackable)) return void 0;
  context.stack.add(trackable);
  try {
    const { schema: inner, optional, nullable, defaultValue } = unwrap(schema);
    if (!inner) {
      if (defaultValue !== void 0) return defaultValue;
      if (nullable) return null;
      if (optional) return void 0;
      return void 0;
    }
    const def = inner._def;
    const typeName = resolveType(def);
    if (defaultValue !== void 0) return defaultValue;
    if (nullable) return null;
    if (optional) return void 0;
    switch (typeName) {
      case "ZodString":
      case "string": {
        const checks = normalizeChecks(def?.checks);
        for (const check of checks) {
          if (!check.kind && !check.extra?.format) continue;
          if (check.kind === "uuid" || check.extra?.format === "uuid") return DEFAULT_EXAMPLE_VALUES.uuid;
          if (check.kind === "email" || check.extra?.format === "email") return DEFAULT_EXAMPLE_VALUES.email;
          if (check.kind === "url" || check.extra?.format === "url" || check.extra?.format === "uri") return DEFAULT_EXAMPLE_VALUES.url;
          if (check.kind === "datetime" || check.extra?.format === "date-time") return DEFAULT_EXAMPLE_VALUES.datetime;
        }
        return DEFAULT_EXAMPLE_VALUES.string;
      }
      case "ZodNumber":
      case "number": {
        const checks = normalizeChecks(def?.checks);
        const isInt = checks.some((check) => check.kind === "int" || check.kind === "isInteger");
        return isInt ? DEFAULT_EXAMPLE_VALUES.integer : DEFAULT_EXAMPLE_VALUES.number;
      }
      case "ZodBigInt":
      case "bigint":
        return BigInt(1);
      case "ZodBoolean":
      case "boolean":
        return DEFAULT_EXAMPLE_VALUES.boolean;
      case "ZodEnum":
      case "enum": {
        const entries = def?.values ?? def?.entries;
        const values = Array.isArray(entries) ? entries : entries ? Object.values(entries) : [];
        return values[0];
      }
      case "ZodNativeEnum": {
        const values = Object.values(def?.values || []);
        return values[0];
      }
      case "ZodLiteral":
      case "literal":
        return def?.value ?? (Array.isArray(def?.values) ? def.values[0] : void 0);
      case "ZodArray":
      case "array": {
        const elementSchema = def?.type && typeof def.type === "object" ? def.type : def?.element && typeof def.element === "object" ? def.element : void 0;
        const child = generateExample(elementSchema, context);
        return child === void 0 ? [] : [child];
      }
      case "ZodTuple":
      case "tuple": {
        const items = def?.items || [];
        return items.map((item) => generateExample(item, context));
      }
      case "ZodObject":
      case "object": {
        const shape = getShape(def);
        const obj = {};
        for (const [key, value] of Object.entries(shape)) {
          const example = generateExample(value, context);
          if (example !== void 0) obj[key] = example;
        }
        return obj;
      }
      case "ZodRecord":
      case "record": {
        const valueExample = generateExample(def?.valueType ?? def?.value, context);
        return valueExample === void 0 ? {} : { key: valueExample };
      }
      case "ZodUnion":
      case "union": {
        const options = def?.options || [];
        return options.length ? generateExample(options[0], context) : void 0;
      }
      case "ZodPipeline":
      case "pipe":
        return generateExample(def?.out ?? def?.innerType ?? def?.schema, context);
      case "ZodLazy":
      case "lazy": {
        const next = typeof def?.getter === "function" ? def.getter() : void 0;
        return next ? generateExample(next, context) : void 0;
      }
      case "ZodPromise":
      case "promise":
        return generateExample(def?.type, context);
      case "ZodCatch":
      case "catch":
        return generateExample(def?.innerType ?? def?.type, context);
      case "ZodReadonly":
      case "readonly":
        return generateExample(def?.innerType ?? def?.type, context);
      case "ZodIntersection":
      case "intersection": {
        const left = generateExample(def?.left, context);
        const right = generateExample(def?.right, context);
        if (typeof left === "object" && left && typeof right === "object" && right) {
          return { ...left, ...right };
        }
        return left ?? right;
      }
      case "ZodDate":
      case "date":
        return DEFAULT_EXAMPLE_VALUES.datetime;
      default:
        return void 0;
    }
  } finally {
    context.stack.delete(trackable);
  }
}
function buildParameters(schema, location, pathParamNames) {
  if (!schema && location !== "path") return [];
  const params = [];
  if (location === "path" && pathParamNames && pathParamNames.length) {
    const merged = mergePathParamSchemas(schema, pathParamNames);
    for (const { name, schema: paramSchema, optional } of merged) {
      const jsonSchema = zodToJsonSchema(paramSchema);
      const example = generateExample(paramSchema);
      const description = extractZodDescription(paramSchema);
      params.push({
        name,
        in: "path",
        required: !optional,
        schema: jsonSchema ?? { type: "string" },
        example,
        ...description ? { description } : {}
      });
    }
    return params;
  }
  if (!schema) return params;
  const { schema: unwrapped } = unwrap(schema);
  if (!unwrapped) return params;
  const def = unwrapped._def;
  const typeName = resolveType(def);
  if (typeName === "ZodObject" || typeName === "object") {
    const shape = getShape(def);
    for (const [key, raw] of Object.entries(shape)) {
      const details = unwrap(raw);
      if (!details.schema) continue;
      const jsonSchema = zodToJsonSchema(details.schema);
      const example = generateExample(details.schema);
      const description = extractZodDescription(raw);
      params.push({
        name: key,
        in: location,
        required: location === "path" ? true : !details.optional,
        schema: jsonSchema ?? {},
        example,
        ...description ? { description } : {}
      });
    }
  } else {
    const jsonSchema = zodToJsonSchema(unwrapped);
    const example = generateExample(unwrapped);
    const description = extractZodDescription(unwrapped);
    params.push({
      name: location === "header" ? "X-Custom-Header" : "value",
      in: location,
      required: location === "path",
      schema: jsonSchema ?? {},
      example,
      ...description ? { description } : {}
    });
  }
  return params;
}
function mergePathParamSchemas(schema, params) {
  const merged = [];
  const map = {};
  if (schema) {
    const { schema: unwrapped } = unwrap(schema);
    if (unwrapped && unwrapped._def && (resolveType(unwrapped._def) === "ZodObject" || resolveType(unwrapped._def) === "object")) {
      const shape = getShape(unwrapped._def);
      for (const [key, value] of Object.entries(shape)) {
        map[key] = value;
      }
    }
  }
  for (const param of params) {
    merged.push({
      name: param.name,
      schema: map[param.name],
      optional: !!param.optional
    });
  }
  return merged;
}
function buildRequestBody(request) {
  if (!request) return void 0;
  const schema = zodToJsonSchema(request.schema);
  const example = request.example ?? generateExample(request.schema);
  const contentType = request.contentType ?? "application/json";
  return {
    required: true,
    content: {
      [contentType]: {
        schema: schema ?? {},
        example
      }
    },
    description: request.description
  };
}
function buildResponses(method, responses, errors, metadata) {
  const entries = {};
  const list = [...responses ?? []];
  const errorList = [...errors ?? []];
  const inferredList = [];
  if (metadata?.requireAuth) {
    inferredList.push({
      status: 401,
      description: "Unauthorized",
      schema: z.object({ error: z.string() }),
      xAutoGenerated: true
    });
  }
  if (Array.isArray(metadata?.requireFeatures) && metadata.requireFeatures.length) {
    inferredList.push({
      status: 403,
      description: "Forbidden \u2013 missing required features",
      schema: z.object({ error: z.string() }),
      xAutoGenerated: true
    });
  }
  if (!list.some((res) => res.status >= 200 && res.status < 300)) {
    const fallbackStatus = method === "POST" ? 201 : method === "DELETE" ? 204 : 200;
    list.push({
      status: fallbackStatus,
      description: fallbackStatus === 204 ? "Success" : "Success response"
    });
  }
  const setEntry = (res, options) => {
    const status = String(res.status || 200);
    if (options?.skipIfPresent && Object.prototype.hasOwnProperty.call(entries, status)) return;
    const mediaType = res.mediaType ?? "application/json";
    const schema = res.schema ? zodToJsonSchema(res.schema) : void 0;
    const example = res.schema ? res.example ?? generateExample(res.schema) : res.example;
    const isNoContent = res.status === 204;
    entries[status] = {
      description: res.description ?? "",
      ...isNoContent ? {} : {
        content: {
          [mediaType]: {
            schema: schema ?? { type: "object" },
            ...example !== void 0 ? { example } : {}
          }
        }
      },
      ...res.xAutoGenerated ? { "x-autoGenerated": true } : {}
    };
  };
  for (const res of [...list, ...errorList]) setEntry(res);
  for (const res of inferredList) setEntry(res, { skipIfPresent: true });
  return entries;
}
function buildSecurity(metadata, methodDoc, defaults) {
  const securitySchemes = /* @__PURE__ */ new Set();
  if (Array.isArray(methodDoc?.security)) methodDoc.security.forEach((s) => securitySchemes.add(s));
  if (metadata?.requireAuth) securitySchemes.add("bearerAuth");
  if (defaults) defaults.forEach((s) => securitySchemes.add(s));
  if (securitySchemes.size === 0) return void 0;
  return Array.from(securitySchemes.values()).map((scheme) => ({ [scheme]: [] }));
}
function collectExamples(querySchema, bodySchema, pathSchema, headerSchema, metadata) {
  const examples = {};
  const queryExample = querySchema ? generateExample(querySchema) : void 0;
  if (queryExample && typeof queryExample === "object") examples.query = queryExample;
  const bodyExample = bodySchema ? generateExample(bodySchema) : void 0;
  if (bodyExample !== void 0) examples.body = bodyExample;
  const pathExample = pathSchema ? generateExample(pathSchema) : void 0;
  if (pathExample && typeof pathExample === "object") examples.path = pathExample;
  const headerExample = headerSchema ? generateExample(headerSchema) : void 0;
  if (headerExample && typeof headerExample === "object") examples.headers = headerExample;
  if (metadata?.requireAuth) {
    if (!examples.headers) examples.headers = {};
    if (typeof examples.headers.authorization !== "string") {
      examples.headers.authorization = "Bearer <token>";
    }
  }
  return examples;
}
function toFormUrlEncoded(value) {
  if (!value || typeof value !== "object") return "";
  const params = new URLSearchParams();
  for (const [key, raw] of Object.entries(value)) {
    if (raw === void 0) continue;
    params.append(key, raw === null ? "" : String(raw));
  }
  return params.toString();
}
function stringifyBodyExample(value, mediaType) {
  if (value === void 0) return "";
  if (mediaType === "application/x-www-form-urlencoded") {
    return toFormUrlEncoded(value);
  }
  if (!mediaType || mediaType === "application/json") {
    try {
      return JSON.stringify(value, null, 2);
    } catch {
      return "";
    }
  }
  if (typeof value === "string") return value;
  try {
    return JSON.stringify(value, null, 2);
  } catch {
    return String(value);
  }
}
function buildQueryString(example) {
  if (!example || typeof example !== "object") return "";
  const parts = [];
  for (const [key, value] of Object.entries(example)) {
    if (value === void 0 || value === null) continue;
    const encoded = encodeURIComponent(String(value));
    parts.push(`${encodeURIComponent(key)}=${encoded}`);
  }
  return parts.length ? `?${parts.join("&")}` : "";
}
function injectPathExamples(path, params, examples) {
  if (!params.length) return path;
  let result = path;
  for (const param of params) {
    const placeholder = `{${param.name}}`;
    const example = examples && examples[param.name] !== void 0 ? examples[param.name] : `:${param.name}`;
    result = result.replace(placeholder, String(example));
  }
  return result;
}
function buildCurlSample(method, path, params, examples, baseUrl, metadata, requestBody) {
  const lines = [];
  const pathWithExamples = injectPathExamples(path, params, examples.path);
  const query = buildQueryString(examples.query);
  const url = baseUrl.replace(/\/$/, "") + pathWithExamples + query;
  lines.push(`curl -X ${method} "${url}"`);
  lines.push('  -H "Accept: application/json"');
  const headers = { ...examples.headers ?? {} };
  if (metadata?.requireAuth && !headers.Authorization && !headers.authorization) {
    headers.Authorization = "Bearer <token>";
  }
  for (const [key, value] of Object.entries(headers)) {
    lines.push(`  -H "${key.replace(/"/g, "")}: ${String(value).replace(/"/g, "")}"`);
  }
  const bodyExample = examples.body ?? requestBody?.example;
  const requestContentType = requestBody?.contentType ?? "application/json";
  if (bodyExample !== void 0) {
    lines.push(`  -H "Content-Type: ${requestContentType}"`);
    const serialized = stringifyBodyExample(bodyExample, requestContentType);
    if (serialized) {
      const escapedSerialized = escapeShellDoubleQuotes(serialized);
      lines.push(`  -d "${escapedSerialized}"`);
    }
  }
  return lines.join(" \\\n");
}
function escapeShellDoubleQuotes(value) {
  return value.replace(/[\\`"$]/g, "\\$&");
}
function ensureSecurityComponents(doc) {
  if (!doc.components) doc.components = {};
  if (!doc.components.securitySchemes) doc.components.securitySchemes = {};
  if (!doc.components.securitySchemes.bearerAuth) {
    doc.components.securitySchemes.bearerAuth = {
      type: "http",
      scheme: "bearer",
      bearerFormat: "JWT",
      description: "Send an `Authorization: Bearer <token>` header with a valid API token."
    };
  }
}
function resolveOperationId(moduleId, path, method) {
  const cleaned = normalizeOperationIdSegment(path);
  return [moduleId, method.toLowerCase(), cleaned].filter(Boolean).join("_");
}
function normalizeOperationIdSegment(input) {
  let output = "";
  let previousUnderscore = false;
  for (const character of input) {
    const codePoint = character.charCodeAt(0);
    const isLower = codePoint >= 97 && codePoint <= 122;
    const isUpper = codePoint >= 65 && codePoint <= 90;
    const isNumber = codePoint >= 48 && codePoint <= 57;
    const isAlphaNumeric = isLower || isUpper || isNumber;
    if (isAlphaNumeric) {
      output += character;
      previousUnderscore = false;
      continue;
    }
    if (!previousUnderscore) {
      output += "_";
      previousUnderscore = true;
    }
  }
  while (output.startsWith("_")) output = output.slice(1);
  while (output.endsWith("_")) output = output.slice(0, -1);
  return output;
}
function collectRouteDoc(api, moduleId) {
  if ("handlers" in api) {
    const route = api;
    if (route.docs) return route.docs;
    const maybe = route.handlers?.openApi;
    if (maybe && typeof maybe === "object") return maybe;
  } else {
    const legacy = api;
    if (legacy.docs) {
      return {
        methods: { [legacy.method]: legacy.docs }
      };
    }
    const maybe = legacy.handler?.openApi;
    if (maybe && typeof maybe === "object") {
      return {
        methods: { [legacy.method]: maybe }
      };
    }
  }
  return void 0;
}
function buildOpenApiDocument(modules, options = {}) {
  const doc = {
    openapi: "3.1.0",
    info: {
      title: options.title ?? "Open Mercato API",
      version: options.version ?? "1.0.0",
      description: options.description
    },
    servers: options.servers,
    paths: {}
  };
  ensureSecurityComponents(doc);
  const includeAccessControlMetadata = options.includeAccessControlMetadata !== false;
  const tags = /* @__PURE__ */ new Map();
  for (const moduleEntry of modules) {
    const defaultTag = moduleEntry.info?.title ?? toTitle(moduleEntry.id);
    if (defaultTag) tags.set(defaultTag, moduleEntry.info?.description);
    const apis = moduleEntry.apis ?? [];
    for (const api of apis) {
      const routeDoc = collectRouteDoc(api, moduleEntry.id);
      const moduleTag = routeDoc?.tag ?? defaultTag;
      const normalized = normalizePath(api.path ?? api.path ?? "");
      const pathKey = normalized.path;
      if (!doc.paths[pathKey]) doc.paths[pathKey] = {};
      const availableMethods = "handlers" in api ? HTTP_METHODS.filter((method) => typeof api.handlers?.[method] === "function") : [api.method];
      for (const method of availableMethods) {
        const methodLower = method.toLowerCase();
        const existing = doc.paths[pathKey][methodLower];
        if (existing) continue;
        const metadata = "handlers" in api ? api.metadata?.[method] : void 0;
        const methodDoc = routeDoc?.methods?.[method];
        const summary = methodDoc?.summary ?? routeDoc?.summary ?? `${method} ${pathKey}`;
        const baseDescription = methodDoc?.description ?? routeDoc?.description;
        const meta = metadata && typeof metadata === "object" ? metadata : void 0;
        const requireFeatures = Array.isArray(meta?.["requireFeatures"]) ? meta["requireFeatures"] : void 0;
        const requireRoles = Array.isArray(meta?.["requireRoles"]) ? meta["requireRoles"] : void 0;
        const requireAuth = meta?.["requireAuth"] === true;
        const descriptionParts = [];
        if (baseDescription) descriptionParts.push(baseDescription);
        if (includeAccessControlMetadata && Array.isArray(requireFeatures) && requireFeatures.length) {
          descriptionParts.push(`Requires features: ${requireFeatures.join(", ")}`);
        }
        if (includeAccessControlMetadata && Array.isArray(requireRoles) && requireRoles.length) {
          descriptionParts.push(`Requires roles: ${requireRoles.join(", ")}`);
        }
        const querySchema = methodDoc?.query;
        const pathSchema = methodDoc?.pathParams ?? routeDoc?.pathParams;
        const headerSchema = methodDoc?.headers;
        const requestBody = methodDoc?.requestBody;
        const examples = collectExamples(querySchema, requestBody?.schema, pathSchema, headerSchema, metadata);
        const curlSample = buildCurlSample(
          method,
          pathKey,
          normalized.params,
          examples,
          options.baseUrlForExamples ?? "https://api.open-mercato.local",
          metadata,
          requestBody
        );
        doc.paths[pathKey][methodLower] = {
          operationId: methodDoc?.operationId ?? resolveOperationId(moduleEntry.id, pathKey, method),
          summary,
          description: descriptionParts.length ? descriptionParts.join("\n\n") : void 0,
          tags: methodDoc?.tags ?? (moduleTag ? [moduleTag] : void 0),
          deprecated: methodDoc?.deprecated,
          externalDocs: methodDoc?.externalDocs,
          parameters: [
            ...buildParameters(pathSchema, "path", normalized.params),
            ...buildParameters(querySchema, "query"),
            ...buildParameters(headerSchema, "header")
          ].filter(Boolean),
          requestBody: buildRequestBody(requestBody),
          responses: buildResponses(method, methodDoc?.responses, methodDoc?.errors, metadata),
          security: buildSecurity(metadata, methodDoc, options.defaultSecurity),
          "x-codeSamples": methodDoc?.codeSamples ?? [
            {
              lang: "curl",
              label: "cURL",
              source: curlSample
            }
          ],
          ...includeAccessControlMetadata && Array.isArray(requireFeatures) && requireFeatures.length ? { "x-require-features": requireFeatures } : {},
          ...includeAccessControlMetadata && Array.isArray(requireRoles) && requireRoles.length ? { "x-require-roles": requireRoles } : {},
          ...requireAuth ? { "x-require-auth": true } : {},
          ...methodDoc?.extensions ?? {}
        };
      }
    }
  }
  doc.tags = Array.from(tags.entries()).map(([name, description]) => ({
    name,
    description: description ?? void 0
  }));
  return doc;
}
function formatMarkdownTable(rows) {
  if (!rows.length) return "";
  const header = ["Name", "Location", "Type", "Description"];
  const align = ["---", "---", "---", "---"];
  const lines = [
    `| ${header.join(" | ")} |`,
    `| ${align.join(" | ")} |`,
    ...rows.map((row) => `| ${row.join(" | ")} |`)
  ];
  return lines.join("\n");
}
function schemaTypeLabel(schema) {
  if (!schema) return "any";
  if (schema.type) return schema.type;
  if (schema.oneOf) return schema.oneOf.map(schemaTypeLabel).join(" | ");
  if (schema.allOf) return schema.allOf.map(schemaTypeLabel).join(" & ");
  return "any";
}
function schemaHasDetails(schema) {
  if (!schema || typeof schema !== "object") return false;
  const schemaObj = schema;
  if (Array.isArray(schemaObj.enum) && schemaObj.enum.length) return true;
  if (schemaObj.const !== void 0) return true;
  if (typeof schemaObj.format === "string") return true;
  if (Array.isArray(schemaObj.oneOf) && schemaObj.oneOf.some((s) => schemaHasDetails(s))) return true;
  if (Array.isArray(schemaObj.anyOf) && schemaObj.anyOf.some((s) => schemaHasDetails(s))) return true;
  if (Array.isArray(schemaObj.allOf) && schemaObj.allOf.some((s) => schemaHasDetails(s))) return true;
  if (schemaObj.items && schemaHasDetails(schemaObj.items)) return true;
  if (schemaObj.properties && Object.keys(schemaObj.properties).length) return true;
  if (Array.isArray(schemaObj.prefixItems) && schemaObj.prefixItems.some((s) => schemaHasDetails(s))) return true;
  if (schemaObj.type && schemaObj.type !== "object") return true;
  return false;
}
function selectContentVariant(content) {
  if (!content) return void 0;
  const entries = Object.entries(content);
  if (!entries.length) return void 0;
  const preferred = [
    "application/json",
    "application/x-www-form-urlencoded",
    "multipart/form-data",
    "text/plain"
  ];
  for (const mediaType2 of preferred) {
    const match = entries.find(([type]) => type === mediaType2);
    if (match) {
      const [selectedType, entry2] = match;
      return { mediaType: selectedType, entry: entry2 };
    }
  }
  const [mediaType, entry] = entries[0];
  return { mediaType, entry };
}
function formatExampleForDisplay(example, mediaType) {
  if (example === void 0) return null;
  if (mediaType === "application/x-www-form-urlencoded") {
    const encoded = toFormUrlEncoded(example);
    if (!encoded) return null;
    return { value: encoded, language: "text" };
  }
  if (mediaType === "multipart/form-data") {
    if (example && typeof example === "object") {
      const lines = Object.entries(example).map(([key, value]) => {
        const rendered = value === void 0 || value === null ? "" : String(value);
        return `${key}=${rendered}`;
      });
      if (lines.length) {
        return { value: lines.join("\n"), language: "text" };
      }
    }
    if (typeof example === "string") {
      return { value: example, language: "text" };
    }
  }
  if (!mediaType || mediaType === "application/json") {
    try {
      return { value: JSON.stringify(example, null, 2), language: "json" };
    } catch {
      return null;
    }
  }
  if (typeof example === "string") {
    return { value: example, language: "text" };
  }
  try {
    return { value: JSON.stringify(example, null, 2), language: "json" };
  } catch {
    return { value: String(example), language: "text" };
  }
}
function formatSchemaForDisplay(schema) {
  if (!schema) return null;
  try {
    return { value: JSON.stringify(schema, null, 2), language: "json" };
  } catch {
    return null;
  }
}
function generateMarkdownFromOpenApi(doc) {
  const lines = [];
  lines.push(`# ${doc.info.title}`);
  lines.push("");
  lines.push(`Version: ${doc.info.version}`);
  if (doc.info.description) {
    lines.push("");
    lines.push(doc.info.description);
  }
  if (doc.servers && doc.servers.length) {
    lines.push("");
    lines.push("## Servers");
    for (const server of doc.servers) {
      lines.push(`- ${server.url}${server.description ? ` \u2013 ${server.description}` : ""}`);
    }
  }
  const sortedPaths = Object.keys(doc.paths).sort((a, b) => a.localeCompare(b));
  for (const path of sortedPaths) {
    const operations = doc.paths[path];
    const methods = Object.keys(operations).sort((a, b) => a.localeCompare(b));
    for (const method of methods) {
      const op = operations[method];
      lines.push("");
      lines.push(`## ${method.toUpperCase()} \`${path}\``);
      if (op.summary) {
        lines.push("");
        lines.push(op.summary);
      }
      if (op.description) {
        lines.push("");
        lines.push(op.description);
      }
      if (op.tags && op.tags.length) {
        lines.push("");
        lines.push(`**Tags:** ${op.tags.join(", ")}`);
      }
      if (op["x-require-auth"]) {
        lines.push("");
        lines.push(`**Requires authentication.**`);
      }
      if (op["x-require-features"]) {
        lines.push("");
        lines.push(`**Features:** ${op["x-require-features"].join(", ")}`);
      }
      if (op["x-require-roles"]) {
        lines.push("");
        lines.push(`**Roles:** ${op["x-require-roles"].join(", ")}`);
      }
      const parameters = op.parameters ?? [];
      if (parameters.length) {
        lines.push("");
        lines.push("### Parameters");
        const rows = parameters.map((p) => {
          const requiredLabel = p.required ? "Required" : "Optional";
          const descriptionParts = [requiredLabel, p.description].filter(Boolean);
          return [
            p.name,
            p.in,
            schemaTypeLabel(p.schema),
            descriptionParts.join(". ")
          ];
        });
        lines.push(formatMarkdownTable(rows));
      }
      if (op.requestBody) {
        const selection = selectContentVariant(op.requestBody.content);
        if (selection) {
          const { mediaType, entry } = selection;
          const example = entry?.example ?? entry?.examples?.default?.value;
          const formatted = formatExampleForDisplay(example, mediaType);
          const schemaFormatted = entry?.schema && schemaHasDetails(entry.schema) ? formatSchemaForDisplay(entry.schema) : null;
          lines.push("");
          lines.push("### Request Body");
          lines.push("");
          lines.push(`Content-Type: \`${mediaType}\``);
          if (formatted) {
            lines.push("");
            lines.push(`\`\`\`${formatted.language}`);
            lines.push(formatted.value);
            lines.push("```");
          } else if (schemaFormatted) {
            lines.push("");
            lines.push(`\`\`\`${schemaFormatted.language}`);
            lines.push(schemaFormatted.value);
            lines.push("```");
          } else {
            lines.push("");
            lines.push("No example available for this content type.");
          }
        }
      }
      const responses = op.responses ?? {};
      const responseStatuses = Object.keys(responses).sort((a, b) => a.localeCompare(b));
      if (responseStatuses.length) {
        lines.push("");
        lines.push("### Responses");
        for (const status of responseStatuses) {
          const response = responses[status];
          if (response?.["x-autoGenerated"]) continue;
          lines.push("");
          lines.push(`**${status}** \u2013 ${response.description || "Response"}`);
          const selection = selectContentVariant(response.content);
          if (selection) {
            const { mediaType, entry } = selection;
            const example = entry?.example ?? entry?.examples?.default?.value;
            const formatted = formatExampleForDisplay(example, mediaType);
            const schemaFormatted = entry?.schema && schemaHasDetails(entry.schema) ? formatSchemaForDisplay(entry.schema) : null;
            lines.push("");
            lines.push(`Content-Type: \`${mediaType}\``);
            if (formatted) {
              lines.push("");
              lines.push(`\`\`\`${formatted.language}`);
              lines.push(formatted.value);
              lines.push("```");
            } else if (schemaFormatted) {
              lines.push("");
              lines.push(`\`\`\`${schemaFormatted.language}`);
              lines.push(schemaFormatted.value);
              lines.push("```");
            }
          }
        }
      }
      const samples = op["x-codeSamples"];
      if (samples && samples.length) {
        const curl = samples.find((sample) => String(sample.lang).toLowerCase() === "curl") ?? samples[0];
        if (curl?.source) {
          lines.push("");
          lines.push("### Example");
          lines.push("");
          lines.push("```bash");
          lines.push(curl.source);
          lines.push("```");
        }
      }
    }
  }
  return lines.join("\n");
}
export {
  buildOpenApiDocument,
  generateMarkdownFromOpenApi
};
//# sourceMappingURL=generator.js.map
