const path = require("path");

const bodyParser = require("koa-body")({multipart: true});
const fetch = require("node-fetch");
const Koa = require("koa");
const koaPug = require("koa-pug");
const koaRouter = require("koa-router");
const { MysqlError } = require("mysql");

const config = require("./config");
const guestbook = require("./guestbook");
const mindflow = require("./mindflow");
const util = require("../shared/util");
const { URLSearchParams } = require("url");


const articles_meta = {
	"rpi_root_on_external_drive": {
		title: "How to move a root from SD card to external drive on Raspberry Pi",
		description: "How to move a root partition to an ext. drive on Raspberry Pi."
	},
	"setting_up_a_tor_proxy_relay_hiddenserv": {
		title: "Setting up a Tor proxy, relay and hidden service",
		description: "How to setup a Tor proxy, relay and hidden service."
	},
	"setting_up_a_mail_server": {
		title: "Setting up a mail server",
		description: "How to create your own mail server using Postfix and Dovecot."
	},
	"nginx_recipes_and_tips": {
		title: "NGiNX's recipes & tips",
		description: "Tips and recipes for NGiNX webserver."
	},
	"hardening_mikrotik": {
		title: "Hardening Mikrotik",
		description: "How to harden security of your Mikrotik router."
	},
};

async function getProcesses() {
	try {
		let reimu = await fetch("http://reimu.arav.home.arpa:14882/processes").then(r => r.json());
		let sakuya = await fetch("http://sakuya.arav.home.arpa:14882/processes").then(r => r.json());
		return Object.fromEntries(Object.entries(reimu).concat(Object.entries(sakuya)));
	} catch {
		return [];
	}
}

// 127.0.0.1:19322
async function getNewCaptcha() {
	try {
		return await fetch("http://startpage.arav.home.arpa/captcha/").then(r => r.text());
	} catch {
		return null;
	}
}

async function solveCaptcha(id, answer) {
	try {
		let body = new URLSearchParams();
		body.append('answer', answer);
		let result = fetch(`http://startpage.arav.home.arpa/captcha/${id}`, { method: "POST", body: body } );
		return await result.then(r => {
			return r.status == 200;
		});
	} catch {
		return false;
	}
}

function setRoutes() {
	return koaRouter().get('/', async ctx => {
		await ctx.render('index', {
			description: "A homepage of a russian guy Arav. Not just homepage, but FTP, radio, and smth more as well."
		});
	})
	.get('/rss.xml', async ctx => {
		ctx.type = 'xml';
		await ctx.render('rss', {
			doctype: 'xml',
			schemahost: `${ctx.header.schema}://${ctx.header.host}`,
			author: "me@arav.top (Alexander \"Arav\" Andreev)",
			items: await mindflow.getPosts()
		})
	})
	.get('/stuff', async ctx => {
		await ctx.render('stuff', {
			title: "/ Stuff",
			description: "Here I share my programs, scripts, articles, may be other stuff.",
			git_site: util.getServiceByHost(ctx.request.host, "git"),
			files_site: util.getServiceByHost(ctx.request.host, "files")
		})
	})
	.get('/mindflow', async ctx => {
		await ctx.render('mindflow', {
			title: "/ Mindflow",
			description: "Here I will post updates on my infrastructure, my very important opinions and thoughts.",
			files_site: util.getServiceByHost(ctx.request.host, "files"),
			tz: util.getClientTimezone(ctx),
			posts: await mindflow.getPosts()
		})
	})
	.get('/stuff/article/:article', async ctx => {
		if (ctx.params.article) {
			return await ctx.render("articles/" + ctx.params.article, {
				title: "/ Article / " + articles_meta[ctx.params.article].title,
				description: articles_meta[ctx.params.article].description
			});
		} else {
			return ctx.throw(404);
		}
	})
	.get('/about', async ctx => {
		await ctx.render('about', {
			title: "/ About",
			description: "This is a page where I'm telling about me and my home server.",
			files_site: util.getServiceByHost(ctx.request.host, "files"),
			services: await getProcesses()
		})
	})
	.get('/guestbook', async ctx => {
		const page = ctx.request.query.p !== undefined ? ctx.request.query.p : 1;
		const page_size = ctx.request.query.ps !== undefined ? ctx.request.query.ps : guestbook.pageSize;

		let posts;

		try {
			posts = await guestbook.getPosts(page, page_size);
		} catch(err) {
			posts = null;
		}

		return await ctx.render('guestbook', {
			title: "/ Guestbook",
			description: "This is my guestbook. Welcome.",
			tz: util.getClientTimezone(ctx),
			owner: config.guestbook.owner,
			posts: posts,
			pages_count: Math.ceil(await guestbook.getPostsCount() / page_size),
			captcha_id: await getNewCaptcha() });
	})
	.post('/guestbook', bodyParser, async ctx => {
		const post = ctx.request.body;

		post.hide_website = post.hide_website !== undefined;

		try {
			let check = await solveCaptcha(post.captcha_id, post.captcha_answer);
			if (!check)
					throw "CAPTCHA is wrong or expired";

			if (await guestbook.addPost(post))
				ctx.redirect("/guestbook");
		} catch(err) {
			if (typeof err == 'object' && err instanceof MysqlError) {
				ctx.response.status = 500;
				ctx.response.body = { error: `Database failed so your post wasn't added. Here's your message:`, message: post.message };
			} else if (typeof err == 'string') {
				ctx.response.status = 403;
				ctx.response.body = { error: `Reason why your post was rejected is "${err}". Here's your message:`, message: post.message };
			}
		}
	});
}


const app = new Koa();
const pug = new koaPug({
	viewPath: path.join(__dirname, "views"),
	locals: {
		date_: (date, tz) => util.datetime(date, util.date_formats.post_date, tz),
		date_id: (date, tz) => util.datetime(date, util.date_formats.id_date, tz),
		date_rss: date => util.datetime(date, util.date_formats.rss_date, "UTC"),
		rssLink: util.rssLink },
	app: app
});


app.proxy = true;
app
	.use(async (ctx, next) => {
		try {
			await next();
			if (ctx.status !== 200) ctx.throw(ctx.status);
		} catch (err) {
			ctx.status = err.status || 500;
			if (ctx.status === 404)
				await ctx.render('404');
			else if (ctx.status === 403)
				await ctx.render('403', { error: ctx.body });
			else
				await ctx.render('500', { error: ctx.body });
		}
	})
	.use(setRoutes().routes())
	.listen(config.port, config.host);
