var step = require('step');
var assert = require('assert');
var dot = require('dot');
dot.templateSettings.strip = false;
var PSQL = require('cartodb-psql');

var util = require('util');
var BaseController = require('./base');

var cors = require('../middleware/cors');
var userMiddleware = require('../middleware/user');

function AnalysesController(authApi, pgConnection) {
    BaseController.call(this, authApi, pgConnection);
}

util.inherits(AnalysesController, BaseController);

module.exports = AnalysesController;

AnalysesController.prototype.register = function(app) {
    app.get(app.base_url_mapconfig + '/analyses/catalog', cors(), userMiddleware, this.catalog.bind(this));
};

AnalysesController.prototype.sendResponse = function(req, res, resource) {
    res.set('Cache-Control', 'public,max-age=10,must-revalidate');
    this.send(req, res, resource, 200);
};

AnalysesController.prototype.catalog = function(req, res) {
    var self = this;
    var username = req.context.user;

    step(
        function reqParams() {
            self.req2params(req, this);
        },
        function catalogQuery(err) {
            assert.ifError(err);
            var pg = new PSQL(dbParamsFromReqParams(req.params));
            getMetadata(username, pg, this);
        },
        function prepareResponse(err, results) {
            assert.ifError(err);

            var analysisIdToTable = results.tables.reduce(function(analysisIdToTable, table) {
                var analysisId = table.relname.split('_')[2];
                if (analysisId && analysisId.length === 40) {
                    analysisIdToTable[analysisId] = table;
                }
                return analysisIdToTable;
            }, {});

            var catalogWithTables = results.catalog.map(function(analysis) {
                if (analysisIdToTable.hasOwnProperty(analysis.node_id)) {
                    analysis.table = analysisIdToTable[analysis.node_id];
                }
                return analysis;
            });

            return catalogWithTables.sort(function(analysisA, analysisB) {
                if (!!analysisA.table && !!analysisB.table) {
                    return analysisB.table.size - analysisA.table.size;
                }

                if (!!analysisA.table) {
                    return -1;
                }

                if (!!analysisB.table) {
                    return 1;
                }

                return -1;
            });
        },
        function sendResponse(err, catalogWithTables) {
            if (err) {
                if (err.message.match(/permission\sdenied/)) {
                    err = new Error('Unauthorized');
                    err.http_status = 401;
                }
                self.sendError(req, res, err);
            } else {
                self.sendResponse(req, res, { catalog: catalogWithTables });
            }
        }
    );
};

var catalogQueryTpl = dot.template(
    'SELECT analysis_def->>\'type\' as type, * FROM cartodb.cdb_analysis_catalog WHERE username = \'{{=it._username}}\''
);

var tablesQueryTpl = dot.template([
    "WITH analysis_tables AS (",
    "    SELECT",
    "        n.nspname AS nspname,",
    "        c.relname AS relname,",
    "        pg_total_relation_size(",
    "            format('%s.%s', pg_catalog.quote_ident(n.nspname), pg_catalog.quote_ident(c.relname))",
    "        ) AS size,",
    "        format('%s.%s', pg_catalog.quote_ident(nspname), pg_catalog.quote_ident(relname)) AS fully_qualified_name",
    "    FROM pg_catalog.pg_class c, pg_catalog.pg_namespace n",
    "    WHERE c.relnamespace = n.oid",
    "    AND pg_catalog.quote_ident(c.relname) ~ '^analysis_[a-z0-9]{10}_[a-z0-9]{40}$'",
    "    AND n.nspname IN ('{{=it._username}}', 'public')",
    ")",
    "SELECT *, pg_size_pretty(size) as size_pretty",
    "FROM analysis_tables",
    "ORDER BY size DESC"
].join('\n'));


function getMetadata(username, pg, callback) {
    var results = {
        catalog: [],
        tables: []
    };
    step(
        function getCatalog() {
            pg.query(catalogQueryTpl({_username: username}), this, true); // use read-only transaction
        },
        function handleCatalog(err, resultSet) {
            assert.ifError(err);
            resultSet = resultSet || {};
            results.catalog = resultSet.rows || [];
            this();
        },
        function getTables(err) {
            assert.ifError(err);
            pg.query(tablesQueryTpl({_username: username}), this, true); // use read-only transaction
        },
        function handleTables(err, resultSet) {
            assert.ifError(err);
            resultSet = resultSet || {};
            results.tables = resultSet.rows || [];
            this();
        },
        function finish(err) {
            if (err) {
                return callback(err);
            }

            return callback(null, results);
        }
    );
}


function dbParamsFromReqParams(params) {
    var dbParams = {};
    if ( params.dbuser ) {
        dbParams.user = params.dbuser;
    }
    if ( params.dbpassword ) {
        dbParams.pass = params.dbpassword;
    }
    if ( params.dbhost ) {
        dbParams.host = params.dbhost;
    }
    if ( params.dbport ) {
        dbParams.port = params.dbport;
    }
    if ( params.dbname ) {
        dbParams.dbname = params.dbname;
    }
    return dbParams;
}
