import * as core from './core.js';
import * as http from './http.js';
import * as form from './form.js';

var gTokens = {};
var gDatabase = new Database("auth");

function readSession(session) {
	var result = session ? gDatabase.get("session:" + session) : null;

	if (result) {
		result = JSON.parse(result);

		let kRefreshInterval = 1 * 60 * 60 * 1000;
		let now = Date.now();
		if (!result.lastAccess || result.lastAccess < now - kRefreshInterval) {
			result.lastAccess = now;
			writeSession(session, result);
		}
	}

	return result;
}

function writeSession(session, value) {
	gDatabase.set("session:" + session, JSON.stringify(value));
}

function removeSession(session, value) {
	gDatabase.remove("session:" + session);
}

function newSession() {
	var alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
	var result = "";
	for (var i = 0; i < 32; i++) {
		result += alphabet.charAt(Math.floor(Math.random() * alphabet.length));
	}
	return result;
}

function verifyPassword(password, hash) {
	return bCrypt.hashpw(password, hash) == hash;
}

function hashPassword(password) {
	var salt = bCrypt.gensalt(12);
	return bCrypt.hashpw(password, salt);
}

function noAdministrator() {
	return !core.globalSettings || !core.globalSettings.permissions || !Object.keys(core.globalSettings.permissions).some(function(name) {
		return core.globalSettings.permissions[name].indexOf("administration") != -1;
	});
}

function makeAdministrator(name) {
	if (!core.globalSettings.permissions) {
		core.globalSettings.permissions = {};
	}
	if (!core.globalSettings.permissions[name]) {
		core.globalSettings.permissions[name] = [];
	}
	if (core.globalSettings.permissions[name].indexOf("administration") == -1) {
		core.globalSettings.permissions[name].push("administration");
	}
	core.setGlobalSettings(core.globalSettings);
}

function getCookies(headers) {
	var cookies = {};

	if (headers.cookie) {
		var parts = headers.cookie.split(/,|;/);
		for (var i in parts) {
			var equals = parts[i].indexOf("=");
			var name = parts[i].substring(0, equals).trim();
			var value = parts[i].substring(equals + 1).trim();
			cookies[name] = value;
		}
	}

	return cookies;
}

function handler(request, response) {
	var session = getCookies(request.headers).session;
	if (request.uri == "/login") {
		var sessionIsNew = false;
		var loginError;

		var formData = form.decodeForm(request.query);

		if (request.method == "POST" || formData.submit) {
			session = newSession();
			sessionIsNew = true;
			formData = form.decodeForm(utf8Decode(request.body), formData);
			if (formData.submit == "Login") {
				var account = gDatabase.get("user:" + formData.name);
				account = account ? JSON.parse(account) : account;
				if (formData.register == "1") {
					if (!account &&
						formData.password == formData.confirm) {
						writeSession(session, {name: formData.name});
						account = {password: hashPassword(formData.password)};
						gDatabase.set("user:" + formData.name, JSON.stringify(account));
						if (noAdministrator()) {
							makeAdministrator(formData.name);
						}
					} else {
						loginError = "Error registering account.";
					}
				} else {
					if (account &&
						account.password &&
						verifyPassword(formData.password, account.password)) {
						writeSession(session, {name: formData.name});
						if (noAdministrator()) {
							makeAdministrator(formData.name);
						}
					} else {
						loginError = "Invalid username or password.";
					}
				}
			} else {
				// Proceed as Guest
				writeSession(session, {name: "guest"});
			}
		}

		var cookie = "session=" + session + "; path=/; Max-Age=604800; Secure; SameSite=Strict";
		var entry = readSession(session);
		if (entry && formData.return) {
			response.writeHead(303, {"Location": formData.return, "Set-Cookie": cookie});
			response.end();
		} else {
			File.readFile("core/auth.html").then(function(data) {
				var html = utf8Decode(data);
				var contents = "";

				if (entry) {
					if (sessionIsNew) {
						contents += '<div>Welcome back, ' + entry.name + '.</div>\n';
					} else {
						contents += '<div>You are already logged in, ' + entry.name + '.</div>\n';
					}
					contents += '<div><a href="/login/logout">Logout</a></div>\n';
				} else {
					contents += '<form method="POST">\n';
					if (loginError) {
						contents += "<p>" + loginError + "</p>\n";
					}
					contents += '<div id="auth_greeting"><b>Halt.  Who goes there?</b></div>\n'
					contents += '<div id="auth">\n';
					contents += '<div id="auth_login">\n'
					if (noAdministrator()) {
						contents += '<div class="notice">There is currently no administrator.  You will be made administrator.</div>\n';
					}
					contents += '<div><label for="name">Name:</label> <input type="text" id="name" name="name" value=""></div>\n';
					contents += '<div><label for="password">Password:</label> <input type="password" id="password" name="password" value=""></div>\n';
					contents += '<div id="confirmPassword" style="display: none"><label for="confirm">Confirm:</label> <input type="password" id="confirm" name="confirm" value=""></div>\n';
					contents += '<div><input type="checkbox" id="register" name="register" value="1" onchange="showHideConfirm()"> <label for="register">Register a new account</label></div>\n';
					contents += '<div><input id="loginButton" type="submit" name="submit" value="Login"></div>\n';
					contents += '</div>';
					contents += '<div class="auth_or"> - or - </div>';
					contents += '<div id="auth_guest">\n';
					contents += '<input id="guestButton" type="submit" name="submit" value="Proceeed as Guest">\n';
					contents += '</div>\n';
					contents += '</div>\n';
					contents += '</form>';
				}
				var text = html.replace("<!--SESSION-->", contents);
				response.writeHead(200, {"Content-Type": "text/html; charset=utf-8", "Set-Cookie": cookie, "Content-Length": text.length});
				response.end(text);
			}).catch(function(error) {
				response.writeHead(404, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
				response.end("404 File not found");
			});
		}
	} else if (request.uri == "/login/logout") {
		removeSession(session);
		response.writeHead(303, {"Set-Cookie": "session=; path=/; secure; SameSite=Strict; expires=Thu, 01 Jan 1970 00:00:00 GMT", "Location": "/login" + (request.query ? "?" + request.query : "")});
		response.end();
	} else {
		response.writeHead(200, {"Content-Type": "text/plain; charset=utf-8", "Connection": "close"});
		response.end("Hello, " + request.client.peerName + ".");
	}
}

function getPermissions(session) {
	var permissions;
	var entry = readSession(session);
	if (entry) {
		permissions = getPermissionsForUser(entry.name);
		permissions.authenticated = entry.name !== "guest";
	}
	return permissions || {};
}

function getPermissionsForUser(userName) {
	var permissions = {};
	if (core.globalSettings && core.globalSettings.permissions && core.globalSettings.permissions[userName]) {
		for (var i in core.globalSettings.permissions[userName]) {
			permissions[core.globalSettings.permissions[userName][i]] = true;
		}
	}
	return permissions;
}

function query(headers) {
	var session = getCookies(headers).session;
	var entry;
	var autologin = tildefriends.args.autologin;
	if (entry = autologin ? {name: autologin} : readSession(session)) {
		return {session: entry, permissions: autologin ? getPermissionsForUser(autologin) : getPermissions(session)};
	}
}

export { handler, query };