var express = require("express");
var hbs = require('express-hbs');
require('handlebars-form-helpers').register(hbs.handlebars);
var nodemailer = require('nodemailer');
var courtsopenUtils = require('./courtsopenUtils.js');
var bodyParser = require("body-parser");
var app = express();
var winston = require('winston');
winston.add(winston.transports.File, { filename: './logs/courtsopen.log', maxsize: 5000000 });  // 5MB

// Setup email
var transporter = nodemailer.createTransport({
    service: 'Gmail',
    auth: {
        user: 'alertmonitorfl@gmail.com',
        pass: 'ZPW!KdxkdTSmyH99'
    }
});
var mailOptions = {
    from: 'Alert Monitor <alertmonitorfl@gmail.com>',
    //to: 'jody@kaplon.us,don@gettner.com',
    to: 'jody@kaplon.us',
    subject: 'Device did not wish me a GoodMorning or GoodEvening',
};

var pg = require("pg");
var conString = "postgres://courtsopen:courtsopen@db/courtsopen";

app.use(bodyParser.json());                             // Needed for JSON POST requests from Particle Cores.
app.use(bodyParser.urlencoded({ extended: false }));    // Needed for web POST requests from edit form.

// Use `.hbs` for extensions and find partials in `views/partials`.
app.engine('hbs', hbs.express4({
    partialsDir: __dirname + '/views/partials'
}));
app.set('view engine', 'hbs');
app.set('views', __dirname + '/views');
var favicon = require('serve-favicon');
app.use(favicon(__dirname + '/assets/favicon.ico'));  // Try this before setting static dir.
app.use(express.static('assets'));

/*******************************************************************************************************
With this express setup, ordering of routes matters!!! It's 1st-come-1st-served.
If a more specific route is placed after a more general route, the general route will be chosen.
Not like nginx where the most specific match always wins.
Using express.Router() in express-4 might fix this, as would converting to hapi...
But, for now, order routes like this:
    - static routes, ordered more specific to less specific
    - dynamic routes, ordered more specific to less specific
********************************************************************************************************/
app.get('/', function(req, res){
    winston.info("GET /");
   res.render('home', {}, function(err, html) {
        if(err !== null) {
            winston.error(err);
        } else {
            res.send(html);
        }
    });
});

app.post('/', function(req, res){
    var postEvent = req.body.postEvent;
    var source = req.body.source;
    winston.info(req.body);
    // If it's stripped down JSON sent by cell modem, there won't be a req.body.data key w/full-nested JSON as value!!!
    // ???how to fix???
    // If post body includes coreid and published_at, assume it's from a particle devcie and parse accordingly.
    if (req.body.coreid !== undefined && req.body.published_at !== undefined) {
        var innerDataJSON = JSON.parse(req.body.data);
        var status = JSON.stringify(innerDataJSON.status, null, 4).slice(1,-1);
        var coreid = JSON.stringify(req.body.coreid, null, 4).slice(1,-1);
        var pubAt = JSON.stringify(req.body.published_at, null, 4).slice(1,-1);
        pg.connect(conString, function(err, client, done) {
            if(err) { return winston.error('error fetching client from pool', err); }
            client.query(
                "INSERT INTO alerts (origjson, coreid, published_at, status) VALUES ($1, $2, $3, $4);",
                [JSON.stringify(req.body, null, 4), coreid, pubAt, status]
            );
            done();
        });
    } else { // parse for minimal data from cell modem.
        var deviceid = JSON.stringify(req.body.data, null, 4).slice(1,4);
        var statusCode = JSON.stringify(req.body.data, null, 4).slice(5,7);
        var statusFromCode;
        switch (statusCode) {
            case "01": statusFromCode = "Open"; break;
            case "02": statusFromCode = "Closed"; break;
            case "03": statusFromCode = "GoodMorning"; break;
            case "04": statusFromCode = "GoodEvening"; break;
            default: statusFromCode = "UnknownStatus"
        };
        var received_at = new Date(Date.now());
        received_at = received_at.toISOString();
        winston.info(statusFromCode + ' ' + received_at);
        pg.connect(conString, function(err, client, done) {
            if(err) { return winston.error('error fetching client from pool', err); }
            client.query(
                "INSERT INTO alerts (origjson, coreid, published_at, status) VALUES ($1, $2, $3, $4);",
                [JSON.stringify(req.body, null, 4), deviceid, received_at, statusFromCode]
            );
            done();
        });
    }
    res.status(204).send('POST received');
});

app.get('/:loc', function(req, res) {
    var loc = req.params.loc;
    if (loc !== 'tt') {
        res.status(404).send('Not found');
    } else {
        winston.info("GET /tt");
        pg.connect(conString, function(err, client, done) {
            if(err) {
                return winston.error('error fetching client from pool', err);
            }
            var devIndexQry = 
            "select status, published_at " + 
            "from alerts " + 
            "where status in ('Open', 'Closed') " +
            "and coreid = '300029000347343339373536' " +
            "order by published_at desc " +
            "limit 2"
            client.query(devIndexQry, function(err, result) {
                //call `done()` to release the client back to the pool
                done();
                if(err) {
                    return winston.error('error running query', err);
                }
                // Loop over elements in rows array, convert ugly UTC times to pretty local times.
                result.rows.forEach(function(row){
                    row.pubdate = courtsopenUtils.getLocDateFromUTC(row.published_at);
                    row.pubtime = courtsopenUtils.getLocTimeFromUTC(row.published_at);
                    if(row.status.toLowerCase().indexOf('closed') > -1){
                        row.statusclass = 'closed';
                    } else { row.statusclass = 'open'; }
                });
                res.render('index', {values: result.rows}, function(err, html) {
                    if(err !== null) {
                        winston.error(err);
                    } else {
                        res.send(html);
                    }
                });
            });
        });
    }
});

app.get('/:loc/status', function(req, res) {
    var loc = req.params.loc;
    if (loc !== 'tt') {
        res.status(404).send('Not found');
    } else {
        winston.info('GET ' + loc + '/status');
        // Lookup most recent status from DB and return as JSON.
        pg.connect(conString, function(err, client, done) {
            if(err) {
                return winston.error('error fetching client from pool', err);
            }
            var mostRecentStatusQry = 
            "select origjson " + 
            "from alerts " + 
            "where status in ('Open', 'Closed') " +
            "and coreid = '300029000347343339373536' " +
            "order by published_at desc " +
            "limit 1"
            client.query(mostRecentStatusQry, function(err, result) {
                //call `done()` to release the client back to the pool
                done();
                if(err) {
                    return winston.error('error running query', err);
                }
                res.setHeader('Content-Type', 'application/json');
                res.send(JSON.stringify(result.rows[0].origjson));
            });
        });
    }
});

setInterval(function() {
    // Check every hour to see if GoodMorning or GoodEvening has gone missing.
    pg.connect(conString, function(err, client, done) {
        if(err) {
            return winston.error('error fetching client from pool', err);
        }
        var deadManQry = "select published_at from alerts where to_timestamp(published_at, 'YYYY-MM-DD HH24:MI:SS') > (now() - interval '14.5 hours')  limit 1";
        client.query(deadManQry, function(err, result) {
            //call `done()` to release the client back to the pool
            done();
            if(err) {
                return winston.error('error running query', err);
            } else if (result.rowCount  == 0) {
                mailOptions.text = "It's been too long since the last data transmission from device. \n\n";
                winston.info(mailOptions.text);
                // Don't include any other details for now, will need to change DB query to get details on last message received.
                //mailOptions.text = mailOptions.text + 'Status message, ' + status + '\n';
                //mailOptions.text = mailOptions.text + 'Published at, ' + courtsopenUtils.getLocDateFromUTC(pubAt) + ' ' + courtsopenUtils.getLocTimeFromUTC(pubAt) + '\n';
                transporter.sendMail(mailOptions, function(error, info){
                    if(error){
                        winston.error(error);
                    }else{
                        winston.info('Message sent: ' + info.response);
                    }
                });
            }
        });
    });
}, 60 * 60 * 1000);

app.listen(3000, function() {
    winston.info("Started on PORT 3000");
});