var knex = require('knex');
module.exports = (s,config,databaseOptions) => {
    var databaseOptions = {
        client: config.databaseType,
        connection: config.db,
    }
    if(databaseOptions.client.indexOf('sqlite')>-1){
        databaseOptions.client = 'sqlite3';
        databaseOptions.useNullAsDefault = true;
    }
    if(databaseOptions.client === 'sqlite3' && databaseOptions.connection.filename === undefined){
        databaseOptions.connection.filename = __dirname+"/shinobi.sqlite"
    }
    const dateSubtract = function(date, interval, units){
      var ret = date
      var checkRollover = function() { if(ret.getDate() != date.getDate()) ret.setDate(0);};
      switch(interval.toLowerCase()) {
        case 'year'   :  ret.setFullYear(ret.getFullYear() - units); checkRollover();  break;
        case 'quarter':  ret.setMonth(ret.getMonth() - 3*units); checkRollover();  break;
        case 'month'  :  ret.setMonth(ret.getMonth() - units); checkRollover();  break;
        case 'week'   :  ret.setDate(ret.getDate() - 7*units);  break;
        case 'day'    :  ret.setDate(ret.getDate() - units);  break;
        case 'hour'   :  ret.setTime(ret.getTime() - units*3600000);  break;
        case 'minute' :  ret.setTime(ret.getTime() - units*60000);  break;
        case 'second' :default:  ret.setTime(ret.getTime() - units*1000);  break;
      }
      return (new Date(ret))
    }
    const sqlDate = function(value){
        var value = value.toLowerCase()
        var splitValue = value.split(' ')
        var amount = parseFloat(splitValue[0])
        var today = new Date()
        var query
        if(value.indexOf('min') > -1){
            query = dateSubtract(today,'minute',amount)
        }else if(value.indexOf('day') > -1){
            query = dateSubtract(today,'day',amount)
        }else if(value.indexOf('hour') > -1){
            query = dateSubtract(today,'hour',amount)
        }
        return query
    }
    const mergeQueryValues = function(query,values){
        if(!values){values=[]}
        var valuesNotFunction = true;
        if(typeof values === 'function'){
            var values = [];
            valuesNotFunction = false;
        }
        if(values&&valuesNotFunction){
            var splitQuery = query.split('?')
            var newQuery = ''
            splitQuery.forEach(function(v,n){
                newQuery += v
                var value = values[n]
                if(value){
                    if(isNaN(value) || value instanceof Date){
                        newQuery += "'"+value+"'"
                    }else{
                        newQuery += value
                    }
                }
            })
        }else{
            newQuery = query
        }
        return newQuery
    }
    const stringToSqlTime = function(value){
        newValue = new Date(value.replace('T',' '))
        return newValue
    }
    const sqlQuery = function(query,values,onMoveOn){
        if(!values){values=[]}
        if(typeof values === 'function'){
            var onMoveOn = values;
            var values = [];
        }
        if(!onMoveOn){onMoveOn=function(){}}
        var mergedQuery = mergeQueryValues(query,values)
        s.debugLog('s.sqlQuery QUERY',mergedQuery)
        return s.databaseEngine
        .raw(query,values)
        .asCallback(function(err,r){
            if(err){
                console.log('s.sqlQuery QUERY ERRORED',query)
                console.log('s.sqlQuery ERROR',err)
            }
            if(onMoveOn && typeof onMoveOn === 'function'){
                switch(databaseOptions.client){
                    case'sqlite3':
                        if(!r)r=[]
                    break;
                    default:
                        if(r)r=r[0]
                    break;
                }
                onMoveOn(err,r)
            }
        })
    }
    const cleanSqlWhereObject = (where) => {
        const newWhere = {}
        Object.keys(where).forEach((key) => {
            if(key !== '__separator'){
                const value = where[key]
                newWhere[key] = value
            }
        })
        return newWhere
    }
    const processSimpleWhereCondition = (dbQuery,where,didOne) => {
        var whereIsArray = where instanceof Array;
        if(where[0] === 'or' || where.__separator === 'or'){
            if(whereIsArray){
                where.shift()
                dbQuery.orWhere(...where)
            }else{
                where = cleanSqlWhereObject(where)
                dbQuery.orWhere(where)
            }
        }else if(!didOne){
            didOne = true
            whereIsArray ? dbQuery.where(...where) : dbQuery.where(where)
        }else{
            whereIsArray ? dbQuery.andWhere(...where) : dbQuery.andWhere(where)
        }
    }
    const processWhereCondition = (dbQuery,where,didOne) => {
        var whereIsArray = where instanceof Array;
        if(!where[0])return;
        if(where[0] && where[0] instanceof Array){
            dbQuery.where(function() {
                var _this = this
                var didOneInsideGroup = false
                where.forEach((whereInsideGroup) => {
                    processWhereCondition(_this,whereInsideGroup,didOneInsideGroup)
                })
            })
        }else if(where[0] && where[0] instanceof Object){
            dbQuery.where(function() {
                var _this = this
                var didOneInsideGroup = false
                where.forEach((whereInsideGroup) => {
                    processSimpleWhereCondition(_this,whereInsideGroup,didOneInsideGroup)
                })
            })
        }else{
            processSimpleWhereCondition(dbQuery,where,didOne)
        }
    }
    const knexError = (dbQuery,options,err) => {
        console.error('knexError----------------------------------- START')
        if(config.debugLogVerbose && config.debugLog === true){
            s.debugLog('s.knexQuery QUERY',JSON.stringify(options,null,3))
            s.debugLog('STACK TRACE, NOT AN ',new Error())
        }
        console.error(err)
        console.error(dbQuery.toString())
        console.error('knexError----------------------------------- END')
    }
    const knexQuery = (options,callback) => {
        try{
            if(!s.databaseEngine)return// console.log('Database Not Set');
            // options = {
            //     action: "",
            //     columns: "",
            //     table: ""
            // }
            var dbQuery
            switch(options.action){
                case'select':
                    options.columns = options.columns.indexOf(',') === -1 ? [options.columns] : options.columns.split(',');
                    dbQuery = s.databaseEngine.select(...options.columns).from(options.table)
                break;
                case'count':
                    options.columns = options.columns.indexOf(',') === -1 ? [options.columns] : options.columns.split(',');
                    dbQuery = s.databaseEngine(options.table)
                    dbQuery.count(options.columns)
                break;
                case'update':
                    dbQuery = s.databaseEngine(options.table).update(options.update)
                break;
                case'delete':
                    dbQuery = s.databaseEngine(options.table)
                break;
                case'insert':
                    dbQuery = s.databaseEngine(options.table).insert(options.insert)
                break;
            }
            if(options.where instanceof Array){
                var didOne = false;
                options.where.forEach((where) => {
                    processWhereCondition(dbQuery,where,didOne)
                })
            }else if(options.where instanceof Object){
                dbQuery.where(options.where)
            }
            if(options.action === 'delete'){
                dbQuery.del()
            }
            if(options.orderBy){
                dbQuery.orderBy(...options.orderBy)
            }
            if(options.groupBy){
                dbQuery.groupBy(options.groupBy)
            }
            if(options.limit){
                if(`${options.limit}`.indexOf(',') === -1){
                    dbQuery.limit(options.limit)
                }else{
                    const limitParts = `${options.limit}`.split(',')
                    dbQuery.limit(limitParts[1]).offset(limitParts[0])
                }
            }
            if(config.debugLog === true){
                console.log(dbQuery.toString())
            }
            if(callback || options.update || options.insert || options.action === 'delete'){
                dbQuery.asCallback(function(err,r) {
                    if(err){
                        knexError(dbQuery,options,err)
                    }
                    if(callback)callback(err,r)
                    if(config.debugLogVerbose && config.debugLog === true){
                        s.debugLog('s.knexQuery QUERY',JSON.stringify(options,null,3))
                        s.debugLog('s.knexQuery RESPONSE',JSON.stringify(r,null,3))
                        s.debugLog('STACK TRACE, NOT AN ',new Error())
                    }
                })
            }
            return dbQuery
        }catch(err){
            if(callback)callback(err,[])
            knexError(dbQuery,options,err)
        }
    }
    const knexQueryPromise = (options) => {
        return new Promise((resolve,reject) => {
            knexQuery(options,(err,rows) => {
                resolve({
                    ok: !err,
                    err: err,
                    rows: rows,
                })
            })
        })
    }
    const initiateDatabaseEngine = () => {
        s.databaseEngine = knex(databaseOptions)
        return s.databaseEngine
    }
    return {
        dateSubtract: dateSubtract,
        sqlDate: sqlDate,
        mergeQueryValues: mergeQueryValues,
        stringToSqlTime: stringToSqlTime,
        sqlQuery: sqlQuery,
        cleanSqlWhereObject: cleanSqlWhereObject,
        processSimpleWhereCondition: processSimpleWhereCondition,
        processWhereCondition: processWhereCondition,
        knexError: knexError,
        knexQuery: knexQuery,
        knexQueryPromise: knexQueryPromise,
        initiateDatabaseEngine: initiateDatabaseEngine
    }
}
