require('./lib/Extensions').install();
import * as express from 'express';
import * as bodyParser from 'body-parser';
import __rootDir from './lib/RootDirFinder';
import { c } from './lib/Log';
import db from './models/Database';
import Doc, { ALL_MODELS, ModelId } from './models/Doc';
import { config } from './lib/config';
import { hbs } from './lib/Hbs';
const app = express();
const PORT = process.env.PORT || config.appPort;
const __frontDir = __rootDir+`/front`;

// Express setup
app.set('trust proxy', 'loopback');
app.disable('x-powered-by');
app.use(bodyParser.json());
app.set('views', `${__frontDir}/views`);
app.set('view engine', 'hbs');
app.engine('hbs', hbs.__express);

const staticHandler = express.static(__frontDir);
const staticHandlerNoHtml: express.RequestHandler = function(req, res, next) {
	if (req.path === '/' || req.path.endsWith('.html') || req.path.endsWith('.hbs')) {
		return next();
	} else {
		return staticHandler(req, res, next);
	}
};
app.get('/favicon.ico', (req, res) => res.sendFile(`${__frontDir}/favicon.ico`));
app.get('/robots.txt',  (req, res) => res.sendFile(`${__frontDir}/robots.txt`));
app.use('/front', staticHandlerNoHtml);
/// ^^ in production, those aren't used b/c we serve static files from nginx.

/**
 * Routes(html)
 */

app.get('/', async function(req, res) {
	const docs = await db.docs
		.find<Doc>({ featured: true })
		.sort({ _id: -1 })
		.limit(10)
		.project({ shortId: true, title: true })
		.toArray()
	;
	/// ^^ cache this query at some point for performance.
	
	res.render('landing', {
		body_classes: `landing`,
		docs
	});
});

app.get('/model/:model', function(req, res) {
	const model = req.params.model;
	if (! ['arxiv-nlp', 'distil-gpt2', 'ctrl', 'pplm'].includes(model)) {
		return res.sendStatus(404);
	}
	res.render('model', {
		body_classes: `model`,
		model,
		meta: {
			/// ^^ do NOT call this key `layout`.
			title:    (model === 'distil-gpt2')  ? `🐎 DistilGPT-2 model checkpoint`
					: (model === 'ctrl')         ? `☁️ Salesforce Research CTRL`
					: (model === 'pplm')         ? `🚕 Uber AI Plug and Play Language Model`
					:                              `🤓 ArXiv NLP model checkpoint`,
			thumbnail:    (model === 'distil-gpt2') ? 'thumbnail-large-distilgpt2'
						: (model === 'pplm')        ? `thumbnail-large-pplm`
						: undefined,
			path: req.path,
		},
	});
});


app.get('/doc/:model', function(req, res) {
	const model = req.params.model;
	if (! ALL_MODELS.includes(model)) {
		return res.sendStatus(404);
	}
	
	const doc = Doc.seed(model as ModelId);
	/// ^^ new document. It doesn't exist in the db yet,
	/// will only be stored if user presses save.
	
	res.render('index', {
		body_classes: `app`,
		editable: true,
		doc,
	});
});

app.get('/doc/:model/:id/edit', async function(req, res) {
	const model = req.params.model;
	if (! ALL_MODELS.includes(model)) {
		return res.sendStatus(404);
	}
	
	const doc = await Doc.findOne<Doc>({
		longId: req.params.id,
	});
	if (!doc) {
		return res.sendStatus(404);
	}
	/// Existing document, accessed through its private edit url.
	
	res.render('index', {
		body_classes: `app`,
		editable: true,
		doc,
	});
});

app.get('/share/:shortId', async function(req, res) {
	const doc = await Doc.findOne<Doc>(req.params.shortId);
	if (!doc) {
		return res.sendStatus(404);
	}
	/// Existing document, accessed through its public share url.
	/// CAUTION: Make sure we don't expose the edit url!
	delete doc.longId;
	
	res.render('index', {
		body_classes: `app`,
		editable: false,
		doc,
	});
});


/**
 * Routes(ajax)
 */

app.post('/edit/:model/:longId/:shortId', async function(req, res) {
	const query = {
		shortId: req.params.shortId,
		longId:  req.params.longId,
		model:   req.params.model,
	};
	c.debug(`––`);
	c.log(`Attempting to save doc`, query);
	
	const result = await db.docs.updateOne(
		query,
		{ $set: req.body },
		{ upsert: true }
	);
	c.log(result.result);
	
	res.sendStatus(200);
});

app.post('/duplicate/:shortId', async function(req, res) {
	const doc = await Doc.findOne<Doc>(req.params.shortId);
	if (!doc) {
		return res.sendStatus(404);
	}
	
	c.debug(`––`);
	c.log(`Duplicating doc`, doc.shortId);
	
	const newdoc = await doc.duplicate();
	res.send(newdoc.editUrl);
});


// Start engine.

(async () => {
	await db.connect();
	app.listen(PORT, () => {
		c.debug(`Running on http://localhost:${PORT}`);
	});
})();