import {
  attachTrustedAuthContext
} from "@open-mercato/shared/lib/auth/server";
import {
  findApiRouteManifestMatch,
  getApiRouteManifests
} from "@open-mercato/shared/modules/registry";
import { hasAllFeatures } from "@open-mercato/shared/security/features";
const MUTATION_METHODS = /* @__PURE__ */ new Set(["POST", "PUT", "PATCH", "DELETE"]);
const SYNTHETIC_ORIGIN = "http://internal.local";
function normalizeMethod(method) {
  const upper = method.toUpperCase();
  if (upper === "GET" || upper === "POST" || upper === "PUT" || upper === "PATCH" || upper === "DELETE") {
    return upper;
  }
  throw new Error(`Unsupported method "${method}"`);
}
function normalizePath(path) {
  if (typeof path !== "string" || path.length === 0) return "/";
  const trimmed = path.startsWith("/") ? path : `/${path}`;
  let end = trimmed.length;
  while (end > 1 && trimmed.charCodeAt(end - 1) === 47) end--;
  return trimmed.slice(0, end);
}
function buildUrl(path, query) {
  const url = new URL(`${SYNTHETIC_ORIGIN}/api${normalizePath(path)}`);
  if (query) {
    for (const [key, value] of Object.entries(query)) {
      if (value === null || value === void 0) continue;
      url.searchParams.append(key, String(value));
    }
  }
  return url;
}
function buildAuthEnvelope(ctx) {
  const userId = ctx.userId;
  if (!userId) {
    return { auth: null, status: "invalid" };
  }
  const auth = {
    sub: userId,
    userId,
    tenantId: ctx.tenantId,
    orgId: ctx.organizationId,
    roles: [],
    isSuperAdmin: ctx.isSuperAdmin
  };
  return { auth, status: "authenticated" };
}
function pickHandler(entry, mod, method) {
  const direct = mod[method];
  if (typeof direct === "function") return direct;
  if (entry.kind === "legacy") {
    const fallback = mod.default ?? mod.handler;
    if (typeof fallback === "function") return fallback;
  }
  return null;
}
function extractMethodMetadata(metadata, method) {
  if (!metadata || typeof metadata !== "object") return null;
  const record = metadata;
  const perMethod = record[method];
  const source = perMethod && typeof perMethod === "object" ? perMethod : record;
  const result = {};
  if (typeof source.requireAuth === "boolean") {
    result.requireAuth = source.requireAuth;
  }
  if (Array.isArray(source.requireFeatures)) {
    const features = source.requireFeatures.filter((entry) => typeof entry === "string" && entry.length > 0);
    if (features.length > 0) result.requireFeatures = features;
  }
  return result;
}
async function readResponseBody(res) {
  const contentType = res.headers.get("content-type") ?? "";
  if (contentType.includes("application/json")) {
    try {
      return await res.json();
    } catch {
      return null;
    }
  }
  try {
    const text = await res.text();
    if (!text) return null;
    try {
      return JSON.parse(text);
    } catch {
      return text;
    }
  } catch {
    return null;
  }
}
function failure(statusCode, error, details) {
  const response = { success: false, statusCode, error };
  if (details !== void 0) response.details = details;
  return response;
}
function normalizeError(body) {
  if (body && typeof body === "object" && !Array.isArray(body)) {
    const record = body;
    const message = typeof record.error === "string" ? record.error : typeof record.message === "string" ? record.message : null;
    if (message) {
      const { error: _ignored, message: _ignoredMessage, ...rest } = record;
      void _ignored;
      void _ignoredMessage;
      return Object.keys(rest).length > 0 ? { error: message, details: rest } : { error: message };
    }
  }
  if (typeof body === "string" && body.length > 0) {
    return { error: body };
  }
  return { error: "Request failed" };
}
function createAiApiOperationRunner(ctx, options = {}) {
  let manifestPromise = null;
  const loadManifest = async () => {
    if (options.apiRoutes) return options.apiRoutes;
    if (manifestPromise) return manifestPromise;
    const loader = options.loadApiRoutes ?? defaultLoadApiRoutes;
    manifestPromise = loader();
    try {
      return await manifestPromise;
    } catch (error) {
      manifestPromise = null;
      throw error;
    }
  };
  return {
    async run(request) {
      let method;
      try {
        method = normalizeMethod(request.method);
      } catch (error) {
        const message = error instanceof Error ? error.message : "Invalid method";
        return failure(400, message);
      }
      const path = normalizePath(request.path);
      let routes;
      try {
        routes = await loadManifest();
      } catch (error) {
        const message = error instanceof Error ? error.message : "Failed to load API route manifest";
        return failure(500, `Operation runner manifest unavailable: ${message}`);
      }
      const match = findApiRouteManifestMatch(routes, method, path);
      if (!match) {
        return failure(
          404,
          `No documented API route matches ${method} ${path}`
        );
      }
      let mod;
      try {
        mod = await match.route.load();
      } catch (error) {
        const message = error instanceof Error ? error.message : "Failed to load route module";
        return failure(500, `Failed to load route module: ${message}`);
      }
      if (!("openApi" in mod) || mod.openApi === void 0 || mod.openApi === null) {
        return failure(
          501,
          `Route ${method} ${path} is undocumented (missing openApi export); refusing to call from AI tool`
        );
      }
      const handler = pickHandler(match.route, mod, method);
      if (!handler) {
        return failure(
          405,
          `Route ${path} does not export a handler for method ${method}`
        );
      }
      const methodMetadata = extractMethodMetadata(mod.metadata, method);
      const routeFeatures = methodMetadata?.requireFeatures ?? [];
      const isMutation = MUTATION_METHODS.has(method);
      if (isMutation && routeFeatures.length === 0 && !request.allowFeaturelessMutation) {
        return failure(
          403,
          `Mutation route ${method} ${path} declares no requiredFeatures; refusing to call without allowFeaturelessMutation opt-in`
        );
      }
      const toolFeatures = ctx.tool.requiredFeatures ?? [];
      if (routeFeatures.length > 0 && !hasAllFeatures(toolFeatures, routeFeatures)) {
        return failure(
          403,
          `AI tool "${ctx.tool.name}" requiredFeatures do not cover route ${method} ${path} requiredFeatures`,
          { toolFeatures, routeFeatures }
        );
      }
      const url = buildUrl(path, request.query);
      const headers = new Headers();
      const requestInit = { method, headers };
      if (request.body !== void 0 && method !== "GET") {
        headers.set("content-type", "application/json");
        requestInit.body = JSON.stringify(request.body);
      }
      const syntheticRequest = new Request(url, requestInit);
      attachTrustedAuthContext(syntheticRequest, buildAuthEnvelope(ctx));
      let response;
      try {
        response = await handler(syntheticRequest, { params: match.params });
      } catch (error) {
        const message = error instanceof Error ? error.message : "Route handler threw";
        return failure(500, message);
      }
      const status = response.status;
      const body = await readResponseBody(response);
      if (status >= 200 && status < 300) {
        return {
          success: true,
          statusCode: status,
          data: body
        };
      }
      const normalized = normalizeError(body);
      return {
        success: false,
        statusCode: status,
        error: normalized.error,
        ...normalized.details !== void 0 ? { details: normalized.details } : {}
      };
    }
  };
}
async function defaultLoadApiRoutes() {
  const registered = getApiRouteManifests();
  if (registered.length === 0) {
    throw new Error(
      "No API route manifest registered. Call registerApiRouteManifests(...) at app bootstrap or pass apiRoutes/loadApiRoutes to createAiApiOperationRunner."
    );
  }
  return registered;
}
export {
  createAiApiOperationRunner,
  normalizePath
};
//# sourceMappingURL=ai-api-operation-runner.js.map
