import { Result } from "@repo/rdx-utils";
import { ILogger } from "../../logger";
import { ITransactionManager } from "./transaction-manager.interface";

export abstract class TransactionManager implements ITransactionManager {
  protected _transaction: unknown | null = null;
  protected _isCompleted = false;
  protected readonly logger!: ILogger;

  constructor(logger?: ILogger) {
    // Si no hay logger, usa console adaptado
    this.logger = logger ?? {
      info: (msg, meta) => console.info(msg, meta),
      warn: (msg, meta) => console.warn(msg, meta),
      error: (msg, err) => console.error(msg, err),
      debug: (msg, meta) => console.debug(msg, meta),
    };
  }

  /**
   * 🔹 Inicia una transacción si no hay una activa
   */
  async start(): Promise<void> {
    if (this._transaction) {
      this.logger.error("❌ Transaction already started. Nested transactions are not allowed.", {
        label: "TransactionManager.start",
      });
      throw new Error("A transaction is already active. Nested transactions are not allowed.");
    }
    this._transaction = await this._startTransaction();
    this._isCompleted = false;
    this.logger.debug("Transaction started", {
      label: "TransactionManager.start",
    });
  }

  /**
   * 🔹 Devuelve la transacción activa
   */
  getTransaction(): any {
    if (!this._transaction) {
      this.logger.error("❌ No active transaction. Call start() first.", {
        label: "TransactionManager.getTransaction",
      });
      throw new Error("No active transaction. Call start() first.");
    }
    if (this._isCompleted) {
      this.logger.error("❌ Transaction already completed.");
      throw new Error("Transaction already completed.");
    }

    return this._transaction;
  }

  /**
   * 🔹 Ejecuta una función dentro de una transacción
   */
  async complete<T>(work: (transaction: unknown) => Promise<T>): Promise<T> {
    if (this._transaction) {
      this.logger.error(
        "❌ Cannot start a new transaction inside another. Nested transactions are not allowed.",
        { label: "TransactionManager.complete" }
      );
      console.trace();

      throw new Error("A transaction is already active. Nested transactions are not allowed.");
    }

    await this.start();
    try {
      const result = await work(this.getTransaction());
      if (result instanceof Result && result.isFailure) {
        throw result.error;
      }
      await this.commit();
      return result;
    } catch (err) {
      await this.rollback();
      const error = err as Error;
      this.logger.error(`❌ Transaction rolled back due to error: ${error.message}`, {
        //stack: error.stack,
        label: "TransactionManager.start",
      });
      throw error;
    }
  }

  /**
   * 🔹 Métodos abstractos para manejar transacciones
   */
  protected abstract _startTransaction(): Promise<any>;
  protected abstract _commitTransaction(): Promise<void>;
  protected abstract _rollbackTransaction(): Promise<void>;

  async commit(): Promise<void> {
    if (!this._transaction) {
      this.logger.error("❌ No transaction to commit.", { label: "TransactionManager.commit" });
      throw new Error("No transaction to commit.");
    }
    if (this._isCompleted) {
      this.logger.error("❌ Transaction already completed. Cannot commit again.", {
        label: "TransactionManager.commit",
      });
      throw new Error("Transaction already completed.");
    }

    try {
      await this._commitTransaction();
      this.logger.info("Transaction committed.", { label: "TransactionManager.commit" });
    } catch (err) {
      const error = err as Error;
      this.logger.error(`❌ Error during commit: ${error.message}`, {
        stack: error.stack,
        label: "TransactionManager.commit",
      });
      throw error;
    } finally {
      // Siempre limpia el estado interno
      this._transaction = null;
      this._isCompleted = true;
    }
  }

  async rollback(): Promise<void> {
    if (!this._transaction) {
      this.logger.error("❌ No transaction to rollback.", { label: "TransactionManager.rollback" });
      throw new Error("No transaction to rollback.");
    }
    if (this._isCompleted) {
      this.logger.error("❌ Transaction already completed. Cannot rollback again.", {
        label: "TransactionManager.rollback",
      });
      throw new Error("Transaction already completed.");
    }

    try {
      await this._rollbackTransaction();
      this.logger.info("Transaction rolled back.");
    } catch (err) {
      const error = err as Error;
      this.logger.error(`❌ Error during rollback: ${error.message}`, {
        stack: error.stack,
        label: "TransactionManager.rollback",
      });
      throw error;
    } finally {
      this._transaction = null;
      this._isCompleted = true;
    }
  }
}
