import WebServer from "./express";
import Logger from "./logger";
import Redis from "./redis";
import Cassandra from "./cassandra";
import Elastic from "./elastic";
import Mysql from "./mysql";
import Mailer from "./mailer";
import Registry from "./metrics";
import Crashreporter from "./crashreporter";
import Consul from "./consul";


import Global from "./global";
import Settings from "./settings";
import Utils from "./utils";
import * as Index from "./index.d";
import MockObjects from "./mockObjects"
import * as Elasticsearch from 'elasticsearch';

import CassandraMock from "./mocks/cassandraMock";
import RedisMock from "./mocks/redisMock";
import ElasticMock from "./mocks/elasticMock";
import StaticMethods from "./statics";

export interface PromiseOption {
    reject?: (reason?: any) => void;
    resolve?: (value?: any) => void;

    bind?: any;
    args: any[];
    controller?: any;
}

const promBundle = require("express-prom-bundle");
let fs = require("fs");
let redisSetting = {};

let defaultOptions = require("../options.json"); //Default settins anyting start from this

exports.setDefaultFileOptions = function(filename: string): boolean{
    return Settings.Instance.setFile(filename);
};

exports.setDefaultOptions = function(options: Index.ModuleOptions | null = null): void{
    if(options != null){ 
        Settings.Instance.set(options);
    }
};

exports.setEnvMap = function(envMap: Index.EnvMap){
    if(envMap != null){
        Settings.Instance.setEnvMap(envMap);
    }
}

exports.setEnvMapFile = function(filename: string){
    if(filename != null){
        Settings.Instance.setEnvMapFile(filename);
    }
}

exports.setCustomSettings = function(settings: any, token: string = "global"){
    Settings.Instance.setCustom(token, settings);
}

exports.getCustomSettings = function(token: string = "global"): any{
    return Settings.Instance.getCustom(token);
}

exports.getSettings = function(): Settings{
    return Settings.Instance;
}


exports.resetSettings = function(){
    Settings.Instance.reset();
}

exports.resetDefaultOptions = function(options: Index.ModuleOptions | null = null): void{
    defaultOptions = require("../options.json");
    if(options != null){
        defaultOptions = Utils.deepExtend({}, defaultOptions, options);        
    }
    Settings.Instance.set(defaultOptions);
};
exports.setProjectStatus = function(status: () => any): void{
    Global.Instance.personalStatus = status;
}
exports.setProjectFunctionality = function(isFunctional: () => boolean): void{
    Global.Instance.personalIsFunctional = isFunctional;
}
exports.isFunctional = function(): boolean{
    return Global.Instance.isFunctional();
}

exports.express = function(options: Index.ModuleOptions = {}): WebServer{
    let swagger = options.swagger ? options.swagger : null;
    let morgan = options.morgan ? options.morgan : null;
    let zipkin = options.zipkin ? options.zipkin : null;
    
    Settings.Instance.setSwagger("global", swagger);
    Settings.Instance.setMorgan("global", morgan);
    Settings.Instance.setZipkin("global", zipkin);
    
    return new WebServer();
};
exports.express.mock = function(options: Index.ModuleOptions = {}){
    return MockObjects.makeWebServer(options);
}

//Crashreporter
exports.crashreporter = function(token: string = ""): Crashreporter | null{
    if(token == ""){
        return Global.Instance.Crashreporters.get();        
    }else{
        return Global.Instance.Crashreporters.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.crashreporter.configurate = function(options: Index.CrashReporterOptions = {}): void{
    Settings.Instance.setCrashReporter("global", options);
    Global.Instance.Crashreporters.delete();
};

exports.crashreporter.newInstance = function(options: Index.CrashReporterOptions = {}, token: string = ""): Crashreporter{
    Settings.Instance.setCrashReporter(token, options);
    return  Global.Instance.Crashreporters.newInstance(token);
};
exports.crashreporter.deleteInstance = function(token: string): void{
    Global.Instance.Crashreporters.delete(token);
};
exports.crashreporter.reset = function(): void{
    Global.Instance.Crashreporters.reset();
};


//Logger
exports.logger = function(token: string = ""): Logger | null{
    if(token == ""){
        return Global.Instance.Loggers.get();        
    }else{
        return Global.Instance.Loggers.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.logger.configurate = function(options: Index.LoggerOptions = {}): void{
    Settings.Instance.setLogger("global", options);
    Global.Instance.Loggers.delete();
};

exports.logger.newInstance = function(options: Index.LoggerOptions = {}, token: string = ""): Logger{
    Settings.Instance.setLogger(token, options);
    return  Global.Instance.Loggers.newInstance(token);
};
exports.logger.deleteInstance = function(token: string): void{
    Global.Instance.Loggers.delete(token);
};
exports.logger.reset = function(): void{
    Global.Instance.Loggers.reset();
};



//Redis
exports.redis = function(token: string = ""): Redis | null{
    if(token == ""){
        return Global.Instance.Redis.get();        
    }else{
        return Global.Instance.Redis.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.redis.configurate = function(options: Index.RedisOptions = {}): void{
    Settings.Instance.setRedis("global", options);
    Global.Instance.Redis.delete();
};
exports.redis.newInstance = function(options: Index.RedisOptions = {}, token: string = ""): Redis{
    Settings.Instance.setRedis(token, options);
    return  Global.Instance.Redis.newInstance(token);
};
exports.redis.deleteInstance = function(token: string): void{
    Global.Instance.Redis.delete(token);
};
exports.redis.reset = function(): void{
    Global.Instance.Redis.reset();
};
exports.redis.mock = function(options: Index.RedisOptions = {}, customLib?: new () => Index.ExtendedIORedis): Redis{
    return MockObjects.makeRedis(options, customLib);
};


//Cassandras
exports.cassandra = function(token:string = ""): Cassandra | null{
    if(token == ""){
        return Global.Instance.Cassandras.get();        
    }else{
        return Global.Instance.Cassandras.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.cassandra.configurate = function(options: Index.CassandraOptions = {}): void{
    Settings.Instance.setCassandra("global", options);
    Global.Instance.Cassandras.delete();
};
exports.cassandra.newInstance = function(options: Index.CassandraOptions = {}, token:string = ""): Cassandra{
    Settings.Instance.setCassandra(token, options);
    return  Global.Instance.Cassandras.newInstance(token);
};
exports.cassandra.deleteInstance = function(token: string): void{
    Global.Instance.Cassandras.delete(token);
};
exports.cassandra.reset = function(): void{
    Global.Instance.Cassandras.reset();
};
exports.cassandra.mock = function(options: Index.CassandraOptions = {},
    cassandraLibrary?: new (options: Index.CassandraOptions, 
        errorCallback: (err: any) => void, 
        successCallback: () => void, 
        callbackStatus: (err: any) => void) => Index.ExtendedCassandraClient
): Cassandra{
    return MockObjects.makeCassandra(options, cassandraLibrary);
};


//Elastic
exports.elastic = function(token: string = ""): Elastic | null{
    if(token == ""){
        return Global.Instance.Elastics.get();        
    }else{
        return Global.Instance.Elastics.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.elastic.configurate = function(options: Index.ElasticOptions = {}): void{
    Settings.Instance.setElastic("global", options);
    Global.Instance.Elastics.delete();
};
exports.elastic.newInstance = function(options: Index.ElasticOptions = {}, token: string = ""): Elastic{
    Settings.Instance.setElastic(token, options);
    return  Global.Instance.Elastics.newInstance(token);
};
exports.elastic.deleteInstance = function(token: string): void{
    Global.Instance.Elastics.delete(token);
};
exports.elastic.reset = function(): void{
    Global.Instance.Elastics.reset();
};
exports.elastic.mock = function(options: Index.ElasticOptions = {}, customLib?:  new () => Elasticsearch.Client): Elastic{
    return MockObjects.makeElastic(options, customLib);
};


//Mysql
exports.mysql = function(token: string = ""): Mysql | null{
    if(token == ""){
        return Global.Instance.Mysql.get();        
    }else{
        return Global.Instance.Mysql.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.mysql.configurate = function(options: Index.MysqlOptions = {}): void{
    Settings.Instance.setMysql("global", options);
    Global.Instance.Mysql.delete();
};
exports.mysql.newInstance = function(options: Index.MysqlOptions = {}, token: string = ""): Mysql{
    Settings.Instance.setMysql(token, options);
    return  Global.Instance.Mysql.newInstance(token);
};
exports.mysql.deleteInstance = function(token: string): void{
    Global.Instance.Mysql.delete(token);
};
exports.mysql.reset = function(): void{
    Global.Instance.Mysql.reset();
};


//Mailer
exports.mailer = function(token: string = ""): Mailer | null{
    if(token == ""){
        return Global.Instance.Mailers.get();        
    }else{
        return Global.Instance.Mailers.get(token);        
    }
};
//Configurate just global, by overide default setting with new settings
exports.mailer.configurate = function(options: Index.MailerOptions = {}): void{
    Settings.Instance.setMailer("global", options);
    Global.Instance.Mailers.delete();
};
exports.mailer.newInstance = function(options: Index.MailerOptions = {}, token: string = ""): Mailer{
    Settings.Instance.setMailer(token, options);
    return  Global.Instance.Mailers.newInstance(token);
};
exports.mailer.deleteInstance = function(token: string){
    Global.Instance.Mailers.delete(token);
};
exports.mailer.reset = function(){
    Global.Instance.Mailers.reset();
};


//Metrics
exports.metrics = function(token: string = ""): Registry | null{
    if(token == ""){
        return Global.Instance.Metrics.get();        
    }else{
        return Global.Instance.Metrics.get(token);        
    }
};
exports.metrics.newInstance = function(token: string = ""): Registry{
    let options: Index.MetricsOptions = {};
    options.name = token;
    Settings.Instance.setMetrics(token, options);    
    let registry = Global.Instance.Metrics.newInstance(token);
    return  registry;
};
exports.metrics.deleteInstance = function(token: string){
    Global.Instance.Metrics.delete(token);
};
exports.metrics.reset = function(){
    Global.Instance.Metrics.reset();
};

//Metrics
exports.consul = function(token: string = ""): Consul | null{
    if(token == ""){
        return Global.Instance.Consuls.get();        
    }else{
        return Global.Instance.Consuls.get(token);        
    }
};
exports.consul.newInstance = function(token: string = ""): Consul{
    let options: Index.ConsulOptions = {};
    options.name = token;
    Settings.Instance.setConsul(token, options);    
    let consul = Global.Instance.Consuls.newInstance(token);
    return  consul;
};
exports.consul.deleteInstance = function(token: string){
    Global.Instance.Consuls.delete(token);
};
exports.consul.reset = function(){
    Global.Instance.Consuls.reset();
};

exports.mocks = {};

exports.mocks.redis = RedisMock;
exports.mocks.cassandra = CassandraMock;
exports.mocks.elastic = ElasticMock;

//Modules
exports.nock = function(){
    return require("nock");
}
exports.mocha = function(){
    return require("mocha");
}

exports.LOG_LEVEL = Logger.LOG_LEVEL;
exports.AGGREGATORS = Registry.AGGREGATOR;

exports.Utils = Utils;

exports.Static = StaticMethods;
