import { createLogger } from "../../logger.js";
const log = createLogger("search");
const DEFAULT_THRESHOLD = 3;
const DEFAULT_COOLDOWN_MS = 6e4;
const RETRY_BACKOFF_MS = 100;
const breakers = /* @__PURE__ */ new Map();
function getState(name) {
  let s = breakers.get(name);
  if (!s) {
    s = { failures: 0, tripUntil: 0 };
    breakers.set(name, s);
  }
  return s;
}
function isTripped(state) {
  if (state.tripUntil === 0) return false;
  if (Date.now() >= state.tripUntil) {
    log.info("breaker auto-reset", {});
    state.tripUntil = 0;
    state.failures = 0;
    return false;
  }
  return true;
}
function recordFailure(name, threshold, cooldownMs) {
  const state = getState(name);
  state.failures += 1;
  if (state.failures >= threshold && state.tripUntil === 0) {
    state.tripUntil = Date.now() + cooldownMs;
    log.warn("breaker tripped", {
      engine: name,
      failures: state.failures,
      cooldownMs
    });
  }
}
function recordSuccess(name) {
  const state = getState(name);
  state.failures = 0;
  state.tripUntil = 0;
}
function _resetBreakersForTest() {
  breakers.clear();
}
class BreakerOpenError extends Error {
  constructor(name) {
    super(`breaker open for engine ${name}`);
    this.name = "BreakerOpenError";
  }
}
function wrapWithRetryAndBreaker(engine, cfg) {
  const threshold = cfg?.failureThreshold ?? DEFAULT_THRESHOLD;
  const cooldownMs = cfg?.cooldownMs ?? DEFAULT_COOLDOWN_MS;
  return {
    name: engine.name,
    async search(query, options) {
      const state = getState(engine.name);
      if (isTripped(state)) {
        throw new BreakerOpenError(engine.name);
      }
      let lastErr;
      for (let attempt = 1; attempt <= 2; attempt++) {
        try {
          const results = await engine.search(query, options);
          recordSuccess(engine.name);
          return results;
        } catch (err) {
          lastErr = err;
          if (attempt === 1) {
            await new Promise((r) => setTimeout(r, RETRY_BACKOFF_MS));
          }
        }
      }
      recordFailure(engine.name, threshold, cooldownMs);
      throw lastErr instanceof Error ? lastErr : new Error(String(lastErr));
    }
  };
}
async function runEnginesParallel(entries, query, options) {
  const promises = entries.map(async (entry) => {
    const start = Date.now();
    try {
      const results = await entry.engine.search(query, options);
      return {
        engine: entry.engine.name,
        ok: true,
        results,
        latencyMs: Date.now() - start
      };
    } catch (err) {
      const message = err instanceof Error ? err.message : String(err);
      const skipped = err instanceof BreakerOpenError;
      return {
        engine: entry.engine.name,
        ok: false,
        results: [],
        error: message,
        latencyMs: Date.now() - start,
        ...skipped ? { skipped: true } : {}
      };
    }
  });
  return Promise.all(promises);
}
export {
  _resetBreakersForTest,
  runEnginesParallel,
  wrapWithRetryAndBreaker
};
//# sourceMappingURL=engine-base.js.map