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

class Observer {

    private watcher: ConsulType.Watch;
    private services = [];
    private error = null;
    private listener: ((data: any[], error: any) => void) | null = null;

    public constructor(consul: ConsulType.Consul, service: string){
        let self = this;
        this.watcher = consul.watch({
            method: function(data: any, callback: any){
                data.service = service;
                consul.catalog.service.nodes(data, callback);
            }
        });

        this.watcher.on('change', function(data, res){
            self.error = null;
            self.services = data;
            if(self.listener != null){
                self.listener(data, self.error);
            }
        });

        this.watcher.on('error', function(err, res){
            self.services = [];
            self.error = err;
            if(self.listener != null){
                self.listener(self.services, err);
            }
        });
    }

    public getServices(){
        return this.services;
    }

    public getError(clean: boolean = false){
        let error = this.error;
        if(clean){
            this.error = null;
        }
        return error;
    }

    public setListener(listener: (data: any[], error: any) => void){
        this.listener = listener;
    }

    public destroy(){
        this.error = null;
        this.listener = null;
        this.services = [];
        this.watcher.end();
    }
}


export default class Consul implements MultipleInstanceInterface {
    private consul: ConsulType.Consul;
    private settings: Index.ConsulOptions;
    private observers: {[key: string]: Observer} = {};
    

    public constructor(tag: string){
        this.settings = Settings.Instance.getConsul(tag);

        this.consul = new ConsulType({
            host: this.settings.host,
            port: String(this.settings.port),
            secure: this.settings.secure
        });
    }

    public newObserver(name: string, error?: (error: Error) => void): Observer{
        if(this.observers == null) this.observers = {};

        if(this.observers[name] != null){
            if(error != undefined){
                error(new Error("This observer already exists; Returned the existing one!"));
            }
            return this.observers[name];
        }

        let observer = new Observer(this.consul, name);
        this.observers[name] = observer;
        return this.observers[name];
    }

    public deleteObserver(name: string){
        if(this.observers != null && this.observers[name] != null){
            if(typeof this.observers[name].destroy === "function"){
                this.observers[name].destroy();
            }
            delete this.observers[name];
        }
    }

    public observer(name: string): Observer | null{
        if(this.observers == null) return null;
        return this.observers[name];
    }

    destroy(): void{
        if(this.observers != null){
            for (var key in this.observers) {
                let observer = this.observers[key];
                if(typeof observer.destroy === "function"){
                    observer.destroy();               
                }
            }
        }
        this.observers = {};
    };
    getStatus(): string {
        return "";
    };
    isFunctional(): boolean {
        return true;
    };
}