import * as plugins from './plugins.js';

import { SmartdataCollection } from './classes.collection.js';
import { EasyStore } from './classes.easystore.js';

import { logger } from './logging.js';

/**
 * interface - indicates the connection status of the db
 */
export type TConnectionStatus = 'initial' | 'disconnected' | 'connected' | 'failed';

export class SmartdataDb {
  smartdataOptions: plugins.tsclass.database.IMongoDescriptor;
  mongoDbClient: plugins.mongodb.MongoClient;
  mongoDb: plugins.mongodb.Db;
  status: TConnectionStatus;
  statusConnectedDeferred = plugins.smartpromise.defer();
  smartdataCollectionMap = new plugins.lik.ObjectMap<SmartdataCollection<any>>();

  constructor(smartdataOptions: plugins.tsclass.database.IMongoDescriptor) {
    this.smartdataOptions = smartdataOptions;
    this.status = 'initial';
  }

  // easystore
  public async createEasyStore(nameIdArg: string) {
    const easyStore = new EasyStore(nameIdArg, this);
    return easyStore;
  }

  // basic connection stuff ----------------------------------------------

  /**
   * connects to the database that was specified during instance creation
   */
  public async init(): Promise<any> {
    const finalConnectionUrl = this.smartdataOptions.mongoDbUrl
      .replace('<USERNAME>', this.smartdataOptions.mongoDbUser)
      .replace('<username>', this.smartdataOptions.mongoDbUser)
      .replace('<USER>', this.smartdataOptions.mongoDbUser)
      .replace('<user>', this.smartdataOptions.mongoDbUser)
      .replace('<PASSWORD>', this.smartdataOptions.mongoDbPass)
      .replace('<password>', this.smartdataOptions.mongoDbPass)
      .replace('<DBNAME>', this.smartdataOptions.mongoDbName)
      .replace('<dbname>', this.smartdataOptions.mongoDbName);

    this.mongoDbClient = await plugins.mongodb.MongoClient.connect(finalConnectionUrl, {
      maxPoolSize: 100,
      maxIdleTimeMS: 10,
    });
    this.mongoDb = this.mongoDbClient.db(this.smartdataOptions.mongoDbName);
    this.status = 'connected';
    this.statusConnectedDeferred.resolve();
    console.log(`Connected to database ${this.smartdataOptions.mongoDbName}`);
  }

  /**
   * closes the connection to the databse
   */
  public async close(): Promise<any> {
    await this.mongoDbClient.close();
    this.status = 'disconnected';
    logger.log('info', `disconnected from database ${this.smartdataOptions.mongoDbName}`);
  }

  // handle table to class distribution

  public addCollection(SmartdataCollectionArg: SmartdataCollection<any>) {
    this.smartdataCollectionMap.add(SmartdataCollectionArg);
  }

  /**
   * Gets a collection's name and returns a SmartdataCollection instance
   * @param nameArg
   * @returns DbTable
   */
  public async getSmartdataCollectionByName<T>(nameArg: string): Promise<SmartdataCollection<T>> {
    const resultCollection = await this.smartdataCollectionMap.find(async (dbTableArg) => {
      return dbTableArg.collectionName === nameArg;
    });
    return resultCollection;
  }
}