import CircuitBreaker from "opossum";
import { circuitStateGauge } from "../../shared/observability/metrics.js";
import { logger } from "../../shared/observability/logger.js";

/**
 * 熔断器（P6.1，对齐 02-architecture-design.md §9 + workline P6.1）。
 *
 * per-downstream-service 独立 circuit：
 * - iam / core-edu / data-ana / msg
 *
 * 配置：
 * - timeout: 5s（单次调用超时）
 * - errorThresholdPercentage: 50%（错误率阈值）
 * - resetTimeout: 30s（熔断后 30s 半开探测）
 * - volumeThreshold: 10（最少 10 次调用才开始计算）
 *
 * 状态：
 * - closed → 正常调用
 * - open → 熔断开启，直接抛 BFF_PARENT_SERVICE_UNAVAILABLE(503)
 * - halfOpen → 允许 1 次探测调用
 *
 * metrics: parent_bff_circuit_state Gauge { service, state }
 */

const CIRCUIT_OPTIONS: CircuitBreaker.Options = {
  timeout: 5000,
  errorThresholdPercentage: 50,
  resetTimeout: 30000,
  volumeThreshold: 10,
  rollingCountTimeout: 60000,
  rollingCountBuckets: 10,
};

const breakerRegistry = new Map<string, CircuitBreaker>();

/**
 * 获取或创建 per-service 熔断器。
 */
function getBreaker(serviceName: string): CircuitBreaker {
  let breaker = breakerRegistry.get(serviceName);
  if (breaker) return breaker;

  breaker = new CircuitBreaker(
    async (fn: () => Promise<unknown>) => fn(),
    CIRCUIT_OPTIONS,
  );

  breaker.on("open", () => {
    logger.error({ service: serviceName }, "Circuit breaker OPENED");
    circuitStateGauge.set({ service: serviceName, state: "open" }, 1);
    circuitStateGauge.set({ service: serviceName, state: "closed" }, 0);
    circuitStateGauge.set({ service: serviceName, state: "halfOpen" }, 0);
  });

  breaker.on("close", () => {
    logger.info({ service: serviceName }, "Circuit breaker CLOSED (recovered)");
    circuitStateGauge.set({ service: serviceName, state: "open" }, 0);
    circuitStateGauge.set({ service: serviceName, state: "closed" }, 1);
    circuitStateGauge.set({ service: serviceName, state: "halfOpen" }, 0);
  });

  breaker.on("halfOpen", () => {
    logger.warn(
      { service: serviceName },
      "Circuit breaker HALF-OPEN (probing)",
    );
    circuitStateGauge.set({ service: serviceName, state: "open" }, 0);
    circuitStateGauge.set({ service: serviceName, state: "closed" }, 0);
    circuitStateGauge.set({ service: serviceName, state: "halfOpen" }, 1);
  });

  // 初始状态：closed
  circuitStateGauge.set({ service: serviceName, state: "closed" }, 1);
  circuitStateGauge.set({ service: serviceName, state: "open" }, 0);
  circuitStateGauge.set({ service: serviceName, state: "halfOpen" }, 0);

  breakerRegistry.set(serviceName, breaker);
  return breaker;
}

/**
 * 使用熔断器包装异步调用。
 *
 * 熔断开启时抛 BFF_PARENT_SERVICE_UNAVAILABLE(503)。
 */
export async function withCircuitBreaker<T>(
  serviceName: string,
  fn: () => Promise<T>,
): Promise<T> {
  const breaker = getBreaker(serviceName);
  try {
    return (await breaker.fire(fn)) as T;
  } catch (err) {
    if (err instanceof Error && err.message.includes("Breaker is open")) {
      logger.warn(
        { service: serviceName },
        "Circuit breaker is open, rejecting call",
      );
      throw new Error(
        `BFF_PARENT_SERVICE_UNAVAILABLE: ${serviceName} circuit breaker is open`,
      );
    }
    throw err;
  }
}
