const _ = require('lodash');
const {
    HttpStatusError
} = require('./errors');
const {
    parse
} = require('./parser');
const {
    raw
} = require('./transforms');
const Op = require('sequelize').Op;

class ModelHandler {
    constructor(model, defaults = {
        limit: 50,
        offset: 0
    }) {
        this.model = model;
        this.defaults = defaults;
        this.custom = null;
        this.name = this.model.getTableName();
    }

    create() {
        const handle = (req, res, next) => {
            this.model
                .create(req.body)
                .then(respond)
                .catch(next);

            function respond(row) {
                res.status(201);
                res.send(res.transform(row));
            }
        };

        return [
            raw,
            handle
        ];
    }

    get() {
        const handle = (req, res, next) => {
            this
                .findOne(req.params, req.options)
                .then(respond)
                .catch(next);

            function respond(row) {
                if (!row) {
                    throw new HttpStatusError(404, 'Not Found');
                }

                res.send(res.transform(row));
            }
        };

        return [
            raw,
            handle
        ];
    }

    query() {

        const customQuery = (req) => {
            return new Promise((resolve, reject) => {
                // TODO: Corregir y adecuar al nuevo funcionamiento
                // if (req.query.searchPro == 1 && req.query.fIni && req.query.fFin) {
                //     options.where.fecha = {
                //         $between: [new Date(req.query.fIni) || fAct, new Date(req.query.fFin) || fAct]
                //     };
                // }

                if (!req.query.filter || req.query.filter === '') return resolve();
                const condiciones = [];
                const options = {};
                options.attributes = _.keys(this.model.rawAttributes);

                return this.model.describe().then(fields => {
                        for (const key in fields) {
                            const field = fields[key];
                            const obj = {};

                            let aux;
                            let tipo = field.type;
                            let buscar = (options.attributes == null) ? true : options.attributes.indexOf(key) != -1;
                            console.log('Revisndo l interpretacion del searchPRO', req.query.searchPro == 1);
                            if (req.query.searchPro == 1 && buscar && req.query.tipoBus == 'campo') buscar = req.query.campoSel == key;

                            if (req.query.searchPro == 1 && req.query.tipoBus == 'documento') {
                                console.log('SearchPro_Ddocumento'.bgWhite.red);
                                buscar = false;
                                if (key == 'plantilla_valor') {
                                    obj[key] = {
                                        "$ilike": `%${req.query.filter}%`
                                    };
                                    // xfilter.push(obj);
                                }
                            }
                            if (req.query.searchPro == 1 && req.query.tipoBus == 'flujo') {
                                console.log('SearchPro_Ddocumento'.bgWhite.red);

                                buscar = false;
                                if (key == 'grupo') {
                                    obj[key] = req.query.filter;
                                    // obj[i] = { $ilike:"%"+req.query.filter+"%" };
                                    // xfilter.push(obj);
                                }
                            }

                            if (tipo.indexOf('CHARACTER VARYING') > -1) tipo = 'CHARACTER VARYING';
                            switch (tipo) {
                                case 'INTEGER':
                                    aux = parseInt(req.query.filter);
                                    if (!isNaN(aux) && req.query.filter.indexOf("/") == -1) {
                                        obj[key] = aux;
                                    }
                                    break;
                                case 'USER-DEFINED':

                                    aux = req.query.filter;
                                    for (var j in field.special) {
                                        if (field.special[j].toLowerCase().indexOf(aux.toLowerCase()) == 0) {
                                            obj[key] = field.special[j];
                                        }
                                    }
                                    break;
                                case 'TIMESTAMP WITH TIME ZONE':
                                    // Busqueda de fechas del tipo: 2016-10-20, 2016/10/20, 20-10-2016, 20/10/2016.
                                    // TODO: No se puede buscar por el mes.
                                    console.log('TIMESTAMP WITH TIME ZONE __________________________'.magenta.bgWhite);
                                    console.log('Primer procesado de fecha __________________'.magenta.bgWhite, procesarFecha(req.query.filter));
                                    var consulta = procesarFecha(req.query.filter);
                                    if (consulta != false) {
                                        console.log('Fecha'.cyan, procesarFecha(req.query.filter));
                                        obj[key] = procesarFecha(req.query.filter);
                                        // condiciones.push(obj);
                                    }
                                    break;
                                case 'CHARACTER VARYING':
                                    obj[key] = {
                                        "$iLike": `%${req.query.filter}%`
                                    };
                                    break;
                                case 'TEXT':
                                    obj[key] = {
                                        "$iLike": `%${req.query.filter}%`
                                    };
                                    break;
                                case 'ARRAY':
                                    // obj[key] = {"$iLike":`%${req.query.filter}%`};
                                    break;
                                case 'BOOLEAN':
                                    // obj[key] = {"$iLike":`%${req.query.filter}%`};
                                    break;
                                default:
                                    console.log('DEFAULT__________________________'.magenta.bgWhite, tipo);
                                    obj[key] = req.query.filter;
                                    break;
                            }

                            if (obj.hasOwnProperty(key)) {
                                condiciones.push(obj);
                            }



                        }
                    })
                    .then(() => {
                        console.log('Finalizando filtrado custom'.bgCyan.black);
                        this.custom = condiciones;
                        console.log('Checking custom condition'.bgCyan.black, this.custom);
                        return resolve();
                    });
            });
        }
        const handle = (req, res, next) => {
            customQuery(req)
                .then(() => {
                    let flag = false;
                    if (this.custom !== null) {
                        flag = true;
                        req.query.filter = this.custom;
                        this.custom = null;
                    }


                    this
                        .findAndCountAll(req.query, req.options, flag)
                        .then(respond)
                        .catch(next);
                });

            function respond({
                rows,
                start,
                end,
                count
            }) {
                res.set('Content-Range', `${start}-${end}/${count}`);

                if (count > end) {
                    res.status(206);
                } else {
                    res.status(200);
                }

                const response = {
                    total: count,
                    resultado: rows
                }
                res.send(res.transform(response));
            }
        };

        return [
            raw,
            handle
        ];
    }

    remove() {
        const handle = (req, res, next) => {
            this
                .findOne(req.params)
                .then(destroy)
                .then(respond)
                .catch(next);

            function destroy(row) {
                if (!row) {
                    throw new HttpStatusError(404, 'Not Found');
                }

                return row.destroy();
            }

            function respond() {
                res.sendStatus(204);
            }
        };

        return [
            handle
        ];
    }

    update() {
        const handle = (req, res, next) => {
            console.log('[update] iniciando la modificacion ', req.params, req.body);
            this
                .findOne(req.params)
                .then(updateAttributes)
                .then(respond)
                .catch(next);

            function updateAttributes(row) {
                console.log('[update] row', row);
                if (!row) {
                    throw new HttpStatusError(404, 'Not Found');
                }

                // return row.updateAttributes(req.body);
                return row.update(req.body);
            }

            function respond(row) {
                res.send(res.transform(row));
            }
        };

        return [
            raw,
            handle
        ];
    }

    findOne(params, options) {
        if (params.hasOwnProperty('id')) {
            params[`id_${this.name}`] = params.id;
            delete params.id;
        }
        if (options && options.hasOwnProperty('id')) {
            delete options.id;
            delete options.audit_usuario;

        }
        options = _.merge(parse(params, this.model), options);
        return this.model.findOne(options);
    }

    findAndCountAll(params, options, custom) {
        let parsed = parse(params, this.model, custom);

        options = _(parsed)
            .defaults(this.defaults)
            .merge(options)
            .value();
        if (custom) options.where["$or"] = params.filter;
        if (params.page) {
            options.offset = (params.page === 0 ? 0 : params.page - 1) * options.limit;
        }

        return this.model
            .findAndCountAll(options)
            .then(extract);

        function extract({
            count,
            rows
        }) {
            const start = options.offset;
            const end = Math.min(count, options.offset + options.limit);

            return {
                rows,
                start,
                end,
                count
            };
        }
    }
}

/**
      Funcion que procesa una cadena, verifica si tiene el formato de una fecha.
      @param {pCadena} Cadena de texto con formato de fecha.
      @return Retorna:
      EXITO -> un objeto de consulta con formato sequelize.
      FALLO -> false.
    */
function procesarFecha(pCadena) {

    var fecha = new Date(pCadena);
    var anio = null,
        inicio = null,
        fin = null;

    // Identifica el operador usando en la cadena para separar los datos.
    var operador = pCadena.indexOf('-') > -1 ? '-' : pCadena.indexOf('/') > -1 ? '/' : null;

    // Si existe un operador valido en la cadena.
    if (operador != null) {

        // Si la cadena no es valida como fecha, se la invierte.
        if (fecha == 'Invalid Date') {
            fecha = new Date(((pCadena.split(operador)).reverse()).join("-"));
        }
        // Obtine el año.
        anio = fecha.getFullYear();

        // Si existe el año.
        if (anio != null) {
            var vector = pCadena.split(operador)

            // Si la longitud del vector es igual a 3.
            if (vector.length == 3) {
                var indice = vector.indexOf(anio.toString());

                // Si el año existe dentro del vector de la cadena.
                if (indice > -1) {

                    // Armado de la fecha inicio y fecha fin.
                    if (indice == 0) {
                        inicio = vector[0] + "-" + vector[1] + "-" + vector[2];
                        fin = vector[0] + "-" + vector[1] + "-" + (parseInt(vector[2]) + 1);
                    } else if (indice == 2) {
                        inicio = vector[2] + "-" + vector[1] + "-" + vector[0];
                        fin = vector[2] + "-" + vector[1] + "-" + (parseInt(vector[0]) + 1);
                    }

                    // Armado de la respuesta a retornar.
                    var respuesta = {
                        $gte: inicio,
                        $lt: fin
                    };
                    return respuesta;
                } else return false; // Fin condicional indice.
            } else return false; // Fin condicional longitud vector.
        } else return false; // Fin condicional existencia año.
    } else return false; // Fin condicional existencia operador.
}

module.exports = ModelHandler;