import { authorizeFeatures } from "../../security/featurePolicy.js";
import { createLogger } from "../logger/index.js";
const logger = createLogger("shared").child({ component: "commands" });
function buildBlockedError(result, fallbackMessage) {
  const message = result.message ?? fallbackMessage;
  if (typeof result.status !== "number") return { message };
  return { message, status: result.status, body: result.body ?? { error: message } };
}
function matchesCommandPattern(pattern, commandId) {
  if (pattern === "*") return true;
  if (pattern === commandId) return true;
  if (pattern.endsWith(".*")) {
    const prefix = pattern.slice(0, -2);
    return commandId.startsWith(prefix + ".");
  }
  return false;
}
function collectMatching(interceptors, commandId, userFeatures) {
  return interceptors.filter((i) => matchesCommandPattern(i.targetCommand, commandId)).filter((i) => authorizeFeatures(i.features ?? [], { grantedFeatures: userFeatures })).sort((a, b) => (a.priority ?? 50) - (b.priority ?? 50));
}
async function runCommandInterceptorsBefore(interceptors, commandId, input, context, userFeatures) {
  const matching = collectMatching(interceptors, commandId, userFeatures);
  let currentInput = input;
  const metadataByInterceptor = /* @__PURE__ */ new Map();
  for (const interceptor of matching) {
    if (!interceptor.beforeExecute) continue;
    const result = await interceptor.beforeExecute(currentInput, { ...context, commandId });
    if (result?.ok === false) {
      return {
        ok: false,
        error: buildBlockedError(result, `Blocked by command interceptor: ${interceptor.id}`),
        metadataByInterceptor
      };
    }
    if (result?.modifiedInput) {
      currentInput = typeof currentInput === "object" && currentInput ? { ...currentInput, ...result.modifiedInput } : result.modifiedInput;
    }
    if (result?.metadata) {
      metadataByInterceptor.set(interceptor.id, result.metadata);
    }
  }
  const inputChanged = currentInput !== input;
  return {
    ok: true,
    modifiedInput: inputChanged ? currentInput : void 0,
    metadataByInterceptor
  };
}
async function runCommandInterceptorsAfter(interceptors, commandId, input, result, context, userFeatures, metadataByInterceptor) {
  const matching = collectMatching(interceptors, commandId, userFeatures);
  let currentResult = result;
  for (const interceptor of matching) {
    if (!interceptor.afterExecute) continue;
    try {
      const afterResult = await interceptor.afterExecute(
        input,
        currentResult,
        { ...context, commandId, metadata: metadataByInterceptor.get(interceptor.id) }
      );
      if (afterResult?.modifiedResult && typeof currentResult === "object" && currentResult) {
        currentResult = { ...currentResult, ...afterResult.modifiedResult };
      }
    } catch (error) {
      logger.error("Command interceptor afterExecute failed", { interceptorId: interceptor.id, err: error });
    }
  }
  const resultChanged = currentResult !== result;
  return { modifiedResult: resultChanged ? currentResult : void 0 };
}
async function runCommandInterceptorsBeforeUndo(interceptors, commandId, undoContext, context, userFeatures) {
  const matching = collectMatching(interceptors, commandId, userFeatures);
  const metadataByInterceptor = /* @__PURE__ */ new Map();
  for (const interceptor of matching) {
    if (!interceptor.beforeUndo) continue;
    const result = await interceptor.beforeUndo(undoContext, { ...context, commandId });
    if (result?.ok === false) {
      return {
        ok: false,
        error: buildBlockedError(result, `Undo blocked by command interceptor: ${interceptor.id}`),
        metadataByInterceptor
      };
    }
    if (result?.metadata) {
      metadataByInterceptor.set(interceptor.id, result.metadata);
    }
  }
  return { ok: true, metadataByInterceptor };
}
async function runCommandInterceptorsAfterUndo(interceptors, commandId, undoContext, context, userFeatures, metadataByInterceptor) {
  const matching = collectMatching(interceptors, commandId, userFeatures);
  for (const interceptor of matching) {
    if (!interceptor.afterUndo) continue;
    try {
      await interceptor.afterUndo(
        undoContext,
        { ...context, commandId, metadata: metadataByInterceptor.get(interceptor.id) }
      );
    } catch (error) {
      logger.error("Command interceptor afterUndo failed", { interceptorId: interceptor.id, err: error });
    }
  }
}
export {
  matchesCommandPattern,
  runCommandInterceptorsAfter,
  runCommandInterceptorsAfterUndo,
  runCommandInterceptorsBefore,
  runCommandInterceptorsBeforeUndo
};
//# sourceMappingURL=command-interceptor-runner.js.map
