import { extractErrorMessage } from "@bff/core/utils/error.util.js";

/**
 * A rollback action that can be executed during transaction failure recovery.
 */
export interface RollbackAction {
  /** Execute the rollback */
  execute: () => Promise<void>;
  /** Human-readable label for logging (e.g., step ID or index) */
  label: string;
}

/**
 * Logger interface for the RollbackCoordinator.
 * Compatible with nestjs-pino Logger.
 */
export interface RollbackLogger {
  warn(message: string, ...args: unknown[]): void;
  debug(message: string, ...args: unknown[]): void;
  error(message: string, ...args: unknown[]): void;
  log(message: string, ...args: unknown[]): void;
}

/**
 * Coordinates rollback execution across transaction services.
 *
 * Shared patterns:
 * - LIFO (last-in-first-out) execution order
 * - Continue on individual rollback failure (best-effort)
 * - Structured logging of progress and failures
 * - Returns count of successfully executed rollbacks
 *
 * Used by both TransactionService (database transactions with external rollbacks)
 * and DistributedTransactionService (multi-step distributed transactions).
 */
export class RollbackCoordinator {
  /**
   * Execute rollback actions in reverse order (LIFO).
   *
   * Each rollback is attempted independently; a failure in one rollback
   * does not prevent subsequent rollbacks from executing.
   *
   * @param actions - Ordered list of rollback actions (will be executed in reverse)
   * @param transactionId - Transaction identifier for log correlation
   * @param logger - Logger instance for structured output
   * @returns Number of successfully executed rollbacks
   */
  static async executeInReverse(
    actions: readonly RollbackAction[],
    transactionId: string,
    logger: RollbackLogger
  ): Promise<number> {
    if (actions.length === 0) {
      return 0;
    }

    logger.warn(`Executing ${actions.length} rollback actions [${transactionId}]`);

    let rollbacksExecuted = 0;

    for (let i = actions.length - 1; i >= 0; i--) {
      const action = actions[i];
      if (!action) continue;

      try {
        // eslint-disable-next-line no-await-in-loop -- Rollbacks must execute sequentially in LIFO order
        await action.execute();
        rollbacksExecuted++;
        logger.debug(`Rollback ${action.label} completed [${transactionId}]`);
      } catch (rollbackError) {
        logger.error(`Rollback ${action.label} failed [${transactionId}]`, {
          error: extractErrorMessage(rollbackError),
        });
        // Continue with other rollbacks even if one fails
      }
    }

    logger.log(
      `Completed ${rollbacksExecuted}/${actions.length} rollbacks [${transactionId}]`
    );
    return rollbacksExecuted;
  }
}
