import * as Elasticsearch from 'elasticsearch';
import * as Index from './index.d';
import { MultipleInstanceInterface } from "./multipleInstanceInterface";
import Settings from "./settings";

export default class Elastic implements MultipleInstanceInterface {
    private interval: any;        
    private tag: string;
    private config: Index.ElasticOptions;    

    protected elastic: Elasticsearch.Client | null = null;
    protected status: string;
    private waitForStatus: boolean = false;
    

    constructor(tag: string = "tag") {
        this.config = Settings.Instance.getElastic(tag);
        let statusInterval =  this.config.statusCheckInterval;
        if(statusInterval == null || typeof statusInterval != "number" || statusInterval < 1){
            statusInterval = 5000;
        }

        this.status = "";
        this.tag = tag;

        if(this.config.autoConnect){
            this.connect();            
        }

        this.statusCheck();
        this.interval = setInterval(this.statusCheck.bind(this), statusInterval);
    }

    private statusCheck(): void {
        if (this.waitForStatus) return;

        if(this.elastic == null){
            this.status = "disconnected";
            return;
        }

        this.waitForStatus = true; 
        let self = this;       
        this.elastic.ping({
            requestTimeout: 30000,
          }, function (error: any) {
            self.waitForStatus = false;
            self.setStatus(error);
          });
    }

    public destroy(): void{
        clearInterval(this.interval);
        if(this.elastic == null) return;

        this.elastic.close();
        this.elastic = null;
    }

    public connect(): void {
        this.elastic = new Elasticsearch.Client({
            host: this.config.host,
            log: this.config.log,
          });
    }

    protected setStatus(err: any): void{
        if (err) {
            this.status = "error";
        } else {
            this.status = "connected";
        }
    }

    public close(): void{
        if(this.elastic == null) return;

        this.elastic.close();        
    }

    /**
     * return the status of the inner redis connection
     */
    public getStatus(): string{
        return this.status;
    }

    public isFunctional(): boolean{
        return this.getStatus() == "connected";
    }

    private tryToConnect(response: (error: any, response: any) => void): boolean{
        if(this.elastic == null){
            if(this.config.autoConnect){
                this.connect();
                return true;
            }else{
                response("Do not have a connection", null);
                return false;
            }
        }
        return true;
    }

    public index<T>(object: Elasticsearch.IndexDocumentParams<T>, response: (error: any, response: any) => void){
        if(!this.tryToConnect(response)) return;

        if(this.elastic != null){
            this.elastic.index<T>(object, response);            
        }
    }
    public delete(object: Elasticsearch.DeleteDocumentParams, response: (error: any, response: any) => void){
        if(!this.tryToConnect(response)) return; 

        if(this.elastic != null){
            this.elastic.delete(object, response);
        }
    }
    public get<T>(object: Elasticsearch.GetParams, response: (error: any, response: Elasticsearch.GetResponse<T>) => void){
        if(!this.tryToConnect(response)) return;

        if(this.elastic != null){
            this.elastic.get<T>(object, response);
        }
    }

    public search<T>(object: Elasticsearch.SearchParams, response: (error: any, response: Elasticsearch.SearchResponse<T>) => void){
        if(!this.tryToConnect(response)) return;

        if(this.elastic != null){
            this.elastic.search<T>(object, response);
        }
    }
}