import * as Index from "./index.d";
import { MultipleInstanceInterface } from "./multipleInstanceInterface";
import * as MysqlLib from "mysql";
import Settings from "./settings";

/**
 * better control of mysql connection status
 */
const CONNECTION_STATUS = {
    CONNECTED: 'Connected',
    RECONNECTED: 'Reconnected',
    DISCONNECTED: 'Disconnected',
    ERROR: 'Error',
    ERROR_CONNECT: 'Error. Could not connect.',
    DEFAULT: ''
}

export default class Mysql implements MultipleInstanceInterface {
    private interval: any;        
    private config: Index.MysqlOptions;

    protected mysql: MysqlLib.Connection | null = null;
    protected status: string;
    private waitForStatus: boolean = false;
    
    constructor(tag: string = "tag") {
        this.config = Settings.Instance.getMysql(tag);
        let statusInterval =  this.config.statusCheckInterval;
        if(statusInterval == null || typeof statusInterval != "number" || statusInterval < 1){
            statusInterval = 5000;
        }

        this.status = CONNECTION_STATUS.DEFAULT;

        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.mysql == null){
            this.status = CONNECTION_STATUS.DISCONNECTED;
            return;
        }

        this.waitForStatus = true; 
        let self = this;       
        this.mysql.ping({
            sql: "",//Put a basic sql
            timeout: 30000,
          }, function (error: any) {
            self.waitForStatus = false;
            if(error != null){
                self.status = CONNECTION_STATUS.ERROR;
            }else{
                self.status = CONNECTION_STATUS.CONNECTED;
            }
          });
    }

    public destroy(): void{
        clearInterval(this.interval);
        this.closeConnection();
    }

    private closeConnection(){
        if(this.mysql == null) return;
        
        this.mysql.destroy();
        this.mysql = null;
    }

    public connect(): void {
        this.closeConnection();

        const self = this;
        let settings = {
            multipleStatements: this.config.multipleStatements,
            host: this.config.host,
            port: this.config.port,
            database: this.config.database,
            user: this.config.user,
            password: this.config.password
        };
        const connection = MysqlLib.createConnection(settings);

        connection.connect(function(err:any) {
            if (err) {
                self.status = CONNECTION_STATUS.ERROR_CONNECT;
            } else {
                if(self.status == CONNECTION_STATUS.CONNECTED || self.status == CONNECTION_STATUS.DEFAULT)
                    self.status = CONNECTION_STATUS.CONNECTED;
                else
                    self.status = CONNECTION_STATUS.RECONNECTED;
            }
        });

        connection.on("end", function (err:any) {
            self.destroy();
        });

        connection.on("error", function (err:any) {
            self.status = CONNECTION_STATUS.ERROR;
        });

        connection.on("connect", function () {
            if(self.status == CONNECTION_STATUS.CONNECTED || self.status == CONNECTION_STATUS.DEFAULT)
                self.status = CONNECTION_STATUS.CONNECTED;
            else
                self.status = CONNECTION_STATUS.RECONNECTED;
        });

        self.mysql = connection;
    }

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

    public isFunctional(): boolean{
        let _status = this.getStatus();
        return _status == CONNECTION_STATUS.CONNECTED || _status == CONNECTION_STATUS.RECONNECTED;
    }

    private tryToConnect(): boolean{
        if(this.mysql == null){
            if(this.config.autoConnect){
                this.connect();
                return true;
            }else{
                return false;
            }
        }
        return true;
    }

    public query(options: any, values?: any, callback?: any): MysqlLib.Query | null{
        if(!this.tryToConnect()) return null;

        if(this.mysql != null){
            return this.mysql.query(options, values, callback);            
        }else
            return null;
    }
}