import { getApiInterceptorsForRoute } from "./interceptor-registry.js";
import { authorizeFeatures } from "../../security/featurePolicy.js";
import { logInterceptorActivity } from "../umes/interceptor-activity.js";
const DEFAULT_TIMEOUT_MS = 5e3;
function sanitizeObject(input) {
  if (!input || typeof input !== "object") return void 0;
  const clean = Object.fromEntries(Object.entries(input).filter(([, value]) => value !== void 0));
  return Object.keys(clean).length > 0 ? clean : {};
}
function hasRequiredFeatures(features, userFeatures) {
  return authorizeFeatures(features ?? [], { grantedFeatures: userFeatures ?? [] });
}
function timeoutPromise(ms, interceptorId) {
  return new Promise((_, reject) => {
    setTimeout(() => {
      reject(new Error(`INTERCEPTOR_TIMEOUT:${interceptorId}`));
    }, ms);
  });
}
function isTimeoutError(error) {
  return error instanceof Error && error.message.startsWith("INTERCEPTOR_TIMEOUT:");
}
async function runWithTimeout(task, timeoutMs, interceptorId) {
  return Promise.race([task, timeoutPromise(timeoutMs, interceptorId)]);
}
function toErrorBody(interceptorId, error) {
  const body = {
    error: "Internal interceptor error"
  };
  if (process.env.NODE_ENV !== "production") {
    body.interceptorId = interceptorId;
    body.message = error instanceof Error ? error.message : String(error);
  }
  return body;
}
async function runApiInterceptorsBefore(args) {
  const { routePath, method, context } = args;
  let currentRequest = {
    ...args.request,
    body: sanitizeObject(args.request.body),
    query: sanitizeObject(args.request.query),
    headers: { ...args.request.headers ?? {} }
  };
  const metadataByInterceptor = {};
  const interceptors = getApiInterceptorsForRoute(routePath, method);
  for (const entry of interceptors) {
    const interceptor = entry.interceptor;
    if (!interceptor.before) continue;
    if (!hasRequiredFeatures(interceptor.features, context.userFeatures)) continue;
    const startTime = Date.now();
    try {
      const timeoutMs = interceptor.timeoutMs ?? DEFAULT_TIMEOUT_MS;
      const result = await runWithTimeout(
        interceptor.before(currentRequest, context),
        timeoutMs,
        interceptor.id
      );
      const normalized = result ?? { ok: true };
      if (!normalized.ok) {
        const rejectBody = {
          error: normalized.message ?? "Request blocked by API interceptor"
        };
        if (process.env.NODE_ENV !== "production") {
          rejectBody.interceptorId = interceptor.id;
        }
        logInterceptorActivity({
          interceptorId: interceptor.id,
          moduleId: entry.moduleId,
          route: routePath,
          method,
          result: "blocked",
          durationMs: Date.now() - startTime,
          timestamp: Date.now(),
          statusCode: normalized.statusCode ?? 400,
          message: normalized.message
        });
        return {
          ok: false,
          statusCode: normalized.statusCode ?? 400,
          body: rejectBody
        };
      }
      const hasModifications = !!(normalized.headers || normalized.body || normalized.query);
      logInterceptorActivity({
        interceptorId: interceptor.id,
        moduleId: entry.moduleId,
        route: routePath,
        method,
        result: hasModifications ? "modified" : "allowed",
        durationMs: Date.now() - startTime,
        timestamp: Date.now()
      });
      if (normalized.headers) {
        currentRequest = {
          ...currentRequest,
          headers: {
            ...currentRequest.headers,
            ...normalized.headers
          }
        };
      }
      if (normalized.body) {
        currentRequest = {
          ...currentRequest,
          body: sanitizeObject(normalized.body)
        };
      }
      if (normalized.query) {
        currentRequest = {
          ...currentRequest,
          query: sanitizeObject(normalized.query)
        };
      }
      metadataByInterceptor[interceptor.id] = normalized.metadata;
    } catch (error) {
      if (isTimeoutError(error)) {
        const timeoutBody = { error: "Interceptor timeout" };
        if (process.env.NODE_ENV !== "production") {
          timeoutBody.interceptorId = interceptor.id;
        }
        logInterceptorActivity({
          interceptorId: interceptor.id,
          moduleId: entry.moduleId,
          route: routePath,
          method,
          result: "blocked",
          durationMs: Date.now() - startTime,
          timestamp: Date.now(),
          statusCode: 504,
          message: "Interceptor timeout"
        });
        return {
          ok: false,
          statusCode: 504,
          body: timeoutBody
        };
      }
      logInterceptorActivity({
        interceptorId: interceptor.id,
        moduleId: entry.moduleId,
        route: routePath,
        method,
        result: "blocked",
        durationMs: Date.now() - startTime,
        timestamp: Date.now(),
        statusCode: 500,
        message: error instanceof Error ? error.message : String(error)
      });
      return {
        ok: false,
        statusCode: 500,
        body: toErrorBody(interceptor.id, error)
      };
    }
  }
  return {
    ok: true,
    request: currentRequest,
    metadataByInterceptor
  };
}
async function runApiInterceptorsAfter(args) {
  const { routePath, method, context } = args;
  let body = { ...args.response.body ?? {} };
  let headers = { ...args.response.headers ?? {} };
  const interceptors = getApiInterceptorsForRoute(routePath, method);
  for (const entry of interceptors) {
    const interceptor = entry.interceptor;
    if (!interceptor.after) continue;
    if (!hasRequiredFeatures(interceptor.features, context.userFeatures)) continue;
    try {
      const timeoutMs = interceptor.timeoutMs ?? DEFAULT_TIMEOUT_MS;
      const result = await runWithTimeout(
        interceptor.after(
          args.request,
          { statusCode: args.response.statusCode, body, headers },
          { ...context, metadata: args.metadataByInterceptor?.[interceptor.id] }
        ),
        timeoutMs,
        interceptor.id
      );
      if (!result) continue;
      if (result.replace && typeof result.replace === "object") {
        body = { ...result.replace };
      } else if (result.merge && typeof result.merge === "object") {
        body = { ...body, ...result.merge };
      }
    } catch (error) {
      if (isTimeoutError(error)) {
        const timeoutBody = { error: "Interceptor timeout" };
        if (process.env.NODE_ENV !== "production") {
          timeoutBody.interceptorId = interceptor.id;
        }
        return {
          ok: false,
          statusCode: 504,
          body: timeoutBody,
          headers
        };
      }
      return {
        ok: false,
        statusCode: 500,
        body: toErrorBody(interceptor.id, error),
        headers
      };
    }
  }
  return {
    ok: true,
    statusCode: args.response.statusCode,
    body,
    headers
  };
}
export {
  runApiInterceptorsAfter,
  runApiInterceptorsBefore
};
//# sourceMappingURL=interceptor-runner.js.map
