var BaseDataview = require('./base');
var debug = require('debug')('windshaft:dataview:formula');

const countInfinitiesQueryTpl = ctx => `
    SELECT count(1) FROM (${ctx._query}) __cdb_formula_infinities
    WHERE ${ctx._column} = 'infinity'::float OR ${ctx._column} = '-infinity'::float
`;

const countNansQueryTpl = ctx => `
    SELECT count(1) FROM (${ctx._query}) __cdb_formula_nans
    WHERE ${ctx._column} = 'NaN'::float
`;

const filterOutSpecialNumericValuesTpl = ctx => `
    WHERE
        ${ctx._column} != 'infinity'::float
    AND
        ${ctx._column} != '-infinity'::float
    AND
        ${ctx._column} != 'NaN'::float
`;

const formulaQueryTpl = ctx => `
    SELECT
        ${ctx._operation}(${ctx._column}) AS result,
        (SELECT count(1) FROM (${ctx._query}) _cdb_formula_nulls WHERE ${ctx._column} IS NULL) AS nulls_count
        ${ctx._isFloatColumn ? `,(${countInfinitiesQueryTpl(ctx)}) AS infinities_count` : ''}
        ${ctx._isFloatColumn ? `,(${countNansQueryTpl(ctx)}) AS nans_count` : ''}
    FROM (${ctx._query}) __cdb_formula
    ${ctx._isFloatColumn && ctx._operation !== 'count' ? `${filterOutSpecialNumericValuesTpl(ctx)}` : ''}
`;

const VALID_OPERATIONS = {
    count: true,
    avg: true,
    sum: true,
    min: true,
    max: true
};

const TYPE = 'formula';

/**
 {
     type: 'formula',
     options: {
         column: 'name',
         operation: 'count' // count, sum, avg
     }
 }
 */
module.exports = class Formula extends BaseDataview {
    constructor (query, options = {}, queries = {}) {
        super();

        if (typeof options.operation !== 'string') {
            throw new Error(`Formula expects 'operation' in dataview options`);
        }

        if (!VALID_OPERATIONS[options.operation]) {
            throw new Error(`Formula does not support '${options.operation}' operation`)
        }

        if (options.operation !== 'count' && typeof options.column !== 'string') {
            throw new Error(`Formula expects 'column' in dataview options`);
        }

        this.query = query;
        this.queries = queries;
        this.column = options.column || '1';
        this.operation = options.operation;
        this._isFloatColumn = null;
    }


    sql (psql, override, callback) {
        var self = this;

        if (!callback) {
            callback = override;
            override = {};
        }

        if (this._isFloatColumn === null) {
            this._isFloatColumn = false;
            this.getColumnType(psql, this.column, this.queries.no_filters, function (err, type) {
                if (!err && !!type) {
                    self._isFloatColumn = type.float;
                }
                self.sql(psql, override, callback);
            });
            return null;
        }

        var formulaSql = formulaQueryTpl({
            _isFloatColumn: this._isFloatColumn,
            _query: this.query,
            _operation: this.operation,
            _column: this.column
        });

        debug(formulaSql);

        return callback(null, formulaSql);
    }

    format (result) {
        const {
            result = 0,
            nulls_count = 0,
            nans_count = 0,
            infinities_count = 0
        } = result.rows[0] || {};

        return {
            operation: this.operation,
            result,
            nulls: nulls_count,
            nans: nans_count,
            infinities: infinities_count
        };
    }

    getType () {
        return TYPE;
    };

    toString () {
        return JSON.stringify({
            _type: TYPE,
            _query: this.query,
            _column: this.column,
            _operation: this.operation
        });
    }
}

@@ -1,11 +1,13 @@
-var
+const
  BaseDat
@@ -33,19 +33,21 @@
 base');%0A
-var
+const
  debug =
@@ -1843,16 +1843,17 @@
 ration%60)
+;
 %0A       
@@ -2265,19 +2265,21 @@
         
-var
+const
  self = 
@@ -2790,11 +2790,13 @@
     
-var
+const
  for
@@ -3495,17 +3495,16 @@
 E;%0A    %7D
-;
 %0A%0A    to
@@ -3695,10 +3695,11 @@
 ;%0A    %7D%0A
-
 %7D
+;
 %0A

Use const instead of var to declare variables