function QueryTablesApi(pgQueryRunner) {
    this.pgQueryRunner = pgQueryRunner;
}

var affectedTableRegexCache = {
    bbox: /!bbox!/g,
    scale_denominator: /!scale_denominator!/g,
    pixel_width: /!pixel_width!/g,
    pixel_height: /!pixel_height!/g
};

module.exports = QueryTablesApi;


QueryTablesApi.prototype.getAffectedTablesInQuery = function (username, sql, callback) {
    var query = 'SELECT CDB_QueryTablesText($windshaft$' + prepareSql(sql) + '$windshaft$)';

    this.pgQueryRunner.run(username, query, function handleAffectedTablesInQueryRows (err, rows) {
        if (err){
            var msg = err.message ? err.message : err;
            callback(new Error('could not fetch source tables: ' + msg));
            return;
        }

        // This is an Array, so no need to split into parts
        var tableNames = rows[0].cdb_querytablestext;
        return callback(null, tableNames);
    });
};

QueryTablesApi.prototype.getAffectedTablesAndLastUpdatedTime = function (username, sql, callback) {
    var query = [
        'WITH querytables AS (',
            'SELECT * FROM CDB_QueryTablesText($windshaft$' + prepareSql(sql) + '$windshaft$) as tablenames',
        ')',
        'SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max',
        'FROM CDB_TableMetadata m',
        'WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])'
    ].join(' ');

    this.pgQueryRunner.run(username, query, function handleAffectedTablesAndLastUpdatedTimeRows (err, rows) {
        if (err || rows.length === 0) {
            var msg = err.message ? err.message : err;
            callback(new Error('could not fetch affected tables or last updated time: ' + msg));
            return;
        }

        var result = rows[0];

        // This is an Array, so no need to split into parts
        var tableNames = result.tablenames;

        var lastUpdatedTime = result.max || 0;

        callback(null, {
            affectedTables: tableNames,
            lastUpdatedTime: lastUpdatedTime * 1000
        });
    });
};

QueryTablesApi.prototype.getLastUpdatedTime = function (username, tableNames, callback) {
    if (!Array.isArray(tableNames) || tableNames.length === 0) {
        return callback(null, 0);
    }

    var query = [
        'SELECT EXTRACT(EPOCH FROM max(updated_at)) as max',
        'FROM CDB_TableMetadata m WHERE m.tabname = any (ARRAY[',
            tableNames.map(function(t) { return "'" + t + "'::regclass"; }).join(','),
        '])'
    ].join(' ');

    this.pgQueryRunner.run(username, query, function handleLastUpdatedTimeRows (err, rows) {
        if (err) {
            var msg = err.message ? err.message : err;
            return callback(new Error('could not fetch affected tables or last updated time: ' + msg));
        }
        // when the table has not updated_at means it hasn't been changed so a default last_updated is set
        var lastUpdated = 0;
        if (rows.length !== 0) {
            lastUpdated = rows[0].max || 0;
        }

        return callback(null, lastUpdated*1000);
    });
};

function prepareSql(sql) {
    return sql
        .replace(affectedTableRegexCache.bbox, 'ST_MakeEnvelope(0,0,0,0)')
        .replace(affectedTableRegexCache.scale_denominator, '0')
        .replace(affectedTableRegexCache.pixel_width, '1')
        .replace(affectedTableRegexCache.pixel_height, '1')
    ;
}
