import * as z from 'zod';

function toOpenApiPath(path) {
  return path.split("/").map((part) => part.startsWith(":") ? `{${part.slice(1)}}` : part).join("/");
}
function getTypeFromZodType(zodType) {
  if (zodType instanceof z.ZodString) return "string";
  if (zodType instanceof z.ZodNumber) return "number";
  if (zodType instanceof z.ZodBoolean) return "boolean";
  if (zodType instanceof z.ZodArray) return "array";
  if (zodType instanceof z.ZodObject) return "object";
  const type = zodType.type;
  if (type === "string") return "string";
  if (type === "number") return "number";
  if (type === "boolean") return "boolean";
  if (type === "array") return "array";
  if (type === "object") return "object";
  return "string";
}
function processZodType(zodType) {
  if (zodType instanceof z.ZodOptional) {
    const innerType = zodType._def?.innerType || zodType.unwrap?.();
    if (innerType) {
      return processZodType(innerType);
    }
  }
  if (zodType instanceof z.ZodNullable) {
    const innerType = zodType._def?.innerType || zodType.unwrap?.();
    if (innerType) {
      const innerSchema = processZodType(innerType);
      return {
        ...innerSchema,
        nullable: true
      };
    }
  }
  if (zodType instanceof z.ZodDefault) {
    const innerType = zodType._def?.innerType;
    const defaultValue = zodType._def?.defaultValue?.();
    if (innerType) {
      const innerSchema = processZodType(innerType);
      if (defaultValue !== void 0) {
        return {
          ...innerSchema,
          default: defaultValue
        };
      }
      return innerSchema;
    }
  }
  if (zodType instanceof z.ZodObject) {
    const shape = zodType.shape || zodType._def?.shape?.();
    if (shape) {
      const properties = {};
      const required = [];
      for (const [key, value] of Object.entries(shape)) {
        if (value instanceof z.ZodType) {
          properties[key] = processZodType(value);
          if (!(value instanceof z.ZodOptional)) {
            required.push(key);
          }
        }
      }
      return {
        type: "object",
        properties,
        ...required.length > 0 ? { required } : {}
      };
    }
  }
  if (zodType instanceof z.ZodArray) {
    const elementType = zodType._def?.type || zodType.element;
    return {
      type: "array",
      items: elementType ? processZodType(elementType) : { type: "string" }
    };
  }
  if (zodType instanceof z.ZodEnum) {
    const values = zodType._def?.values || zodType.options;
    return {
      type: "string",
      enum: values
    };
  }
  if (zodType instanceof z.ZodLiteral) {
    const value = zodType._def?.value || zodType.value;
    let type;
    if (value === null) {
      type = "null";
    } else if (value === void 0) {
      return { nullable: true };
    } else {
      type = typeof value;
    }
    return {
      type,
      const: value
    };
  }
  if (zodType instanceof z.ZodUnion) {
    const options = zodType._def?.options || zodType.options;
    if (options && Array.isArray(options)) {
      return {
        oneOf: options.map((opt) => processZodType(opt))
      };
    }
  }
  if (zodType._def?.coerce) {
    const innerType = zodType._def?.innerType;
    if (innerType) {
      return processZodType(innerType);
    }
  }
  return {
    type: getTypeFromZodType(zodType)
  };
}
function getParameters(options) {
  const parameters = [];
  if (options.query instanceof z.ZodObject) {
    const shape = options.query.shape || options.query._def?.shape?.();
    if (shape) {
      for (const [key, value] of Object.entries(shape)) {
        if (value instanceof z.ZodType) {
          parameters.push({
            name: key,
            in: "query",
            required: !(value instanceof z.ZodOptional),
            schema: processZodType(value)
          });
        }
      }
    }
  }
  if (options.params instanceof z.ZodObject) {
    const shape = options.params.shape || options.params._def?.shape?.();
    if (shape) {
      for (const [key, value] of Object.entries(shape)) {
        if (value instanceof z.ZodType) {
          parameters.push({
            name: key,
            in: "path",
            required: true,
            schema: processZodType(value)
          });
        }
      }
    }
  }
  return parameters;
}
function getRequestBody(options) {
  if (!options.body) return void 0;
  if (options.body instanceof z.ZodType) {
    const schema = processZodType(options.body);
    const isOptional = options.body instanceof z.ZodOptional;
    return {
      required: !isOptional,
      content: {
        "application/json": {
          schema
        }
      }
    };
  }
  return void 0;
}
function createErrorSchema() {
  return {
    type: "object",
    properties: {
      message: { type: "string" }
    },
    required: ["message"]
  };
}
function getErrorResponses() {
  return {
    "400": {
      description: "Bad Request",
      content: { "application/json": { schema: createErrorSchema() } }
    },
    "401": {
      description: "Unauthorized",
      content: { "application/json": { schema: createErrorSchema() } }
    },
    "403": {
      description: "Forbidden",
      content: { "application/json": { schema: createErrorSchema() } }
    },
    "404": {
      description: "Not Found",
      content: { "application/json": { schema: createErrorSchema() } }
    },
    "500": {
      description: "Internal Server Error",
      content: { "application/json": { schema: createErrorSchema() } }
    }
  };
}
function generateOpenAPISchema(context, options) {
  const paths = {};
  const tags = [];
  for (const [pluginKey, plugin] of Object.entries(context.plugins)) {
    if (pluginKey === "openApi" || plugin.name === "open-api") {
      continue;
    }
    const pluginRoutes = plugin.routes(context.adapter, context);
    const tagName = pluginKey.charAt(0).toUpperCase() + pluginKey.slice(1);
    tags.push({
      name: tagName,
      description: `${tagName} plugin endpoints`
    });
    for (const [routeKey, endpoint] of Object.entries(pluginRoutes)) {
      const ep = endpoint;
      const path = ep.path;
      const endpointOptions = ep.options || {};
      const method = (endpointOptions.method || "GET").toLowerCase();
      if (!path) continue;
      const openApiPath = toOpenApiPath(path);
      if (!paths[openApiPath]) {
        paths[openApiPath] = {};
      }
      const operation = {
        tags: [tagName],
        operationId: `${pluginKey}_${routeKey}`,
        summary: endpointOptions.metadata?.openapi?.summary,
        description: endpointOptions.metadata?.openapi?.description,
        parameters: getParameters(endpointOptions),
        responses: {
          "200": {
            description: "Successful response",
            content: {
              "application/json": {
                schema: { type: "object" }
              }
            }
          },
          ...getErrorResponses()
        }
      };
      if (["post", "put", "patch"].includes(method)) {
        const requestBody = getRequestBody(endpointOptions);
        if (requestBody) {
          operation.requestBody = requestBody;
        }
      }
      paths[openApiPath][method] = operation;
    }
  }
  return {
    openapi: "3.1.0",
    info: {
      title: options?.title || "BTST API",
      description: options?.description || "API Reference for your BTST application",
      version: options?.version || "1.0.0"
    },
    servers: [
      {
        url: context.basePath,
        description: "API Server"
      }
    ],
    tags,
    paths,
    components: {
      securitySchemes: {
        bearerAuth: {
          type: "http",
          scheme: "bearer",
          description: "Bearer token authentication"
        },
        cookieAuth: {
          type: "apiKey",
          in: "cookie",
          name: "session",
          description: "Session cookie authentication"
        }
      }
    }
  };
}

export { generateOpenAPISchema };
