import { Result } from "@repo/rdx-utils";
import { Sequelize, Transaction } from "sequelize";
import { TransactionManager } from "../database";
import { InfrastructureError, InfrastructureUnavailableError } from "../errors";

export class SequelizeTransactionManager extends TransactionManager {
  protected _database: Sequelize | null = null;
  private readonly isolationLevel: string;

  protected async _startTransaction(): Promise<Transaction> {
    // Sequelize adquiere una conexión del pool y la asocia a la transacción.
    return await this._database!.transaction({
      isolationLevel: Transaction.ISOLATION_LEVELS.READ_COMMITTED,
    });
  }

  protected async _commitTransaction(): Promise<void> {
    if (this._transaction) {
      await this._transaction.commit();
    }
  }

  protected async _rollbackTransaction(): Promise<void> {
    throw new InfrastructureError("[SequelizeTransactionManager] Database not available");
  }

  constructor(database: Sequelize, options?: { isolationLevel?: string }) {
    super();
    this._database = database;
    this.isolationLevel = options?.isolationLevel ?? Transaction.ISOLATION_LEVELS.READ_COMMITTED;
  }

  get database(): Sequelize {
    if (!this._database) {
      throw new InfrastructureUnavailableError(
        "[SequelizeTransactionManager] Database not available"
      );
    }
    return this._database;
  }

  /**
   * Ejecuta el unit of work usando la API gestionada de Sequelize.
   * Esta API adquiere/libera conexiones del pool automáticamente.
   */
  async complete<T>(work: (transaction: Transaction) => Promise<T>): Promise<T> {
    if (!this._database) {
      throw new InfrastructureUnavailableError(
        "[SequelizeTransactionManager] Database not available"
      );
    }

    // Evita transacciones anidadas según la política del TransactionManager base
    if (this._transaction) {
      this.logger.error(
        "❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
        { label: "SequelizeTransactionManager.complete" }
      );
      throw new InfrastructureError(
        "[SequelizeTransactionManager] A transaction is already active. Nested transactions are not allowed."
      );
    }

    try {
      // Usa la forma gestionada: si `work` resuelve => commit, si lanza => rollback.
      const result = await this._database.transaction(async (t) => {
        const workResult = await work(t);
        if (workResult instanceof Result && workResult.isFailure) {
          // Forzamos rollback propagando el error
          throw workResult.error;
        }
        return workResult;
      });
      return result as T;
    } catch (err) {
      const error = err as Error;
      this.logger.error(`❌ Transaction rolled back due to error: ${error.message}`, {
        //stack: error.stack,
        label: "SequelizeTransactionManager.complete",
      });
      throw error;
    }
  }
}
