import { config } from "@/config";
import { initLogger } from "@/infrastructure/logger";
import rTracer from "cls-rtracer";
import Firebird from "node-firebird";
import { IAdapter, IRepositoryQueryBuilder } from "../../domain";
import { FirebirdBusinessTransaction } from "./FirebirdBusinessTransaction";

export interface IFirebirdAdapter extends IAdapter {
  queryBuilder: IRepositoryQueryBuilder;

  startTransaction: () => FirebirdBusinessTransaction;
  disconnect: () => void;
  execute: <T>(query: string, params: any[]) => Promise<T>;
  sync: () => void;
}

export class FirebirdAdapter implements IFirebirdAdapter {
  // eslint-disable-next-line no-use-before-define
  private static instance: FirebirdAdapter;

  public static getInstance(params: {
    queryBuilder: IRepositoryQueryBuilder;
  }): FirebirdAdapter {
    if (!FirebirdAdapter.instance) {
      FirebirdAdapter.instance = FirebirdAdapter.create(params);
    }

    return FirebirdAdapter.instance;
  }

  private static create(params: { queryBuilder: IRepositoryQueryBuilder }) {
    const { queryBuilder } = params;
    const connection = initConnection();

    return new FirebirdAdapter(connection, queryBuilder);
  }

  private _connection: Firebird.ConnectionPool;
  private _queryBuilder: IRepositoryQueryBuilder;

  protected constructor(
    connection: Firebird.ConnectionPool,
    queryBuilder: IRepositoryQueryBuilder,
  ) {
    this._connection = connection;
    this._queryBuilder = queryBuilder;
  }

  get queryBuilder(): IRepositoryQueryBuilder {
    return this._queryBuilder;
  }

  public startTransaction(): FirebirdBusinessTransaction {
    return new FirebirdBusinessTransaction(this._connection).start();
  }

  public disconnect() {
    if (this._connection) {
      this._connection.destroy((err) => {
        if (err) {
          throw err;
        }
      });
      endConnection();
    }
  }

  public async execute<T>(query: string, params: any[] = []): Promise<T> {
    return new Promise((resolve, reject) => {
      this._connection.get((err, db) => {
        if (err) {
          return reject(err);
        }
        db.execute(query, params, (err, result) => {
          if (err) {
            return reject(err);
          }
          db.detach();
          resolve(<T>result);
        });
      });
    });
  }

  public sync() {
    return new Promise((resolve, reject) => {
      this.execute("select current_connection from rdb$database")
        .then(() => resolve(this))
        .catch((error) => reject(error));
    });
  }
}

function initConnection() {
  const { poolCount, ...firebirdOptions } = config.firebird;
  const logger = initLogger(rTracer);

  logger.debug("=========================> CONECTO A FIREBIRD");

  return Firebird.pool(poolCount, firebirdOptions);
}

function endConnection() {
  const logger = initLogger(rTracer);

  logger.debug("<========================= DESCONECTO DE FIREBIRD");
}
