"use strict";

var cmn=require("../$common.js"),
    persist=require("node-persist"),
    crypto=require("crypto"),
    basicAuth=require("basic-auth"),
    fs=require("fs");

var moddir=null;

persist=persist.create({
	dir:cmn.persistdir+"/timetrack",
	continuous:false,
	interval:false
});
persist.initSync();

//events: {"user": [{id: Int,sheet: String,text: String,date: Date,type: String}]}
//  type: "in" / "out"
//accounts: {"user": hash (String)}
var nextid=persist.getItemSync("nextid");
if(nextid==null){
	nextid=1;
	persist.setItemSync("nextid",nextid);
}
var events=persist.getItemSync("events");
(function(){
	if(events==null){
		events={};
		persist.setItemSync("events",events);
	} else {
		for(var user in events){
			for(var ev of events[user]){
				ev.date=new Date(ev.date);
				if(nextid<=ev.id)nextid=ev.id+1;
			}
		}
		persist.setItemSync("nextid",nextid);
	}
})();
var accounts=persist.getItemSync("accounts");
if(accounts==null){
	accounts={};
	persist.setItemSync("accounts",accounts);
}
var naccounts=0;
(function(){
	var user;
	for(user in accounts)naccounts++;
})();


function scryptHash(password,cb){
	crypto.randomBytes(16,function(err,salt){
		if(err){
			cb(err,null);
			return;
		}
		crypto.scrypt(password,salt,32,function(err,key){
			if(err)cb(err,null);
			else cb(null,salt.toString("hex")+"$"+key.toString("hex"));
		});
	});
}

function scryptCompare(password,hash,cb){
	hash=hash.split("$");
	if(hash.length!=2){
		cb(new Error("Invalid hash in database"),null);
		return;
	}
	var salt=Buffer.from(hash[0],"hex"),shash=hash[1];
	crypto.scrypt(password,salt,32,function(err,key){
		if(err)cb(err,null);
		else if(key.toString("hex")==shash)cb(null,true);
		else cb(null,false);
	});
}


function sendUnauth(res){
	res.set("WWW-Authenticate","Basic realm=Authorization required");
	return res.sendStatus(401);
}

function unknownUserHandler(req,res,next){
	res.sendFile(moddir+"/unknownuser.html");
}

function authMiddleware(req,res,next){
	var user=basicAuth(req);
	req.authuser=null;
	if(!user||!user.name){
		sendUnauth(res);
		return;
	}
	req.authuser=user.name;
	if(accounts[req.authuser]){
		scryptCompare(user.pass,accounts[req.authuser],function(err,ok){
			if(ok)next();
			else sendUnauth(res);
		});
	} else {
		unknownUserHandler(req,res,next);
	}
}

function asciiValid(str){
	var i,c;
	for(i=0;i<str.length;i++){
		c=str.charCodeAt(i);
		if(c<32||c>=127)return false;
	}
	return true;
}


module.exports=function(app,io,_moddir){
	moddir=_moddir;

	//first the endpoints that need to bypass authMiddleware
	app.get("/timetrack/authfail",function(req,res){
		sendUnauth(res);
	});
	app.post("/timetrack/createuser",function(req,res){
		var user=basicAuth(req);
		if(!user||!user.name){
			res.status(400).send("No credentials sent");
			return;
		}
		if(user.name.length<3||user.name.length>32||!asciiValid(user.name)){
			res.status(400).send("Invalid username");
			return;
		}
		if(user.pass.length<3||user.pass.length>32||!asciiValid(user.pass)){
			res.status(400).send("Invalid password");
			return;
		}
		if(accounts[user.name]){
			res.status(400).send("User already exists");
			return;
		}
		if(naccounts>=20){
			res.status(500).send("Too many accounts created, please contact Tom...");
			return;
		}
		scryptHash(user.pass,function(err,hash){
			if(!hash){
				res.status(500).send("Something went wrong...");
				console.log(err);
				return;
			}
			accounts[user.name]=hash;
			events[user.name]=[];
			naccounts++;
			persist.setItemSync("accounts",accounts);
			persist.setItemSync("events",events);
			res.status(200).end();
		});
	});

	app.all(["/timetrack","/timetrack/*"],authMiddleware); //for all the other endpoints

	app.get("/timetrack",function(req,res){
		res.sendFile(moddir+"/timetrack.html");
	});
	app.get("/timetrack/list",function(req,res){
		res.json(events[req.authuser]);
	});
	app.get("/timetrack/sheets",function(req,res){
		var seen=new Map();
		var list=[];
		for (var i=0;i<events[req.authuser].length;i++){
			if (!seen.has(events[req.authuser][i].sheet)) {
				seen.set(events[req.authuser][i].sheet, 0);
				list.push(events[req.authuser][i].sheet);
			}
		}
		res.json(list);
	});
	app.delete("/timetrack/event",function(req,res){
		var id=+req.body;
		var i;
		var fail=false;
		var userevents=events[req.authuser];
		if(id<0||~~id!=id||isNaN(id)||!userevents){
			fail=true;
		} else {
			for(i=0;i<userevents.length;i++)if(userevents[i].id==id)break;
			if(i==userevents.length)fail=true;
			else {
				userevents.splice(i,1);
				persist.setItemSync("events",events);
			}
		}
		if(fail)res.status(404).send("Unknown id");
		else res.status(200).end();
	});
	app.post("/timetrack/event",function(req,res){
		var obj;
		try {
			obj=JSON.parse(req.body);
		} catch(e){
			res.status(400).send("Invalid request");
			return;
		}
		var sheet=obj.sheet+"",text=obj.text+"",date=new Date(obj.date),type=obj.type+"";
		if(sheet.length==0||isNaN(date.getTime())||(type!="in"&&type!="out")){
			res.status(400).send("Invalid data");
			return;
		}
		if(events[req.authuser].length>=4000){
			res.status(400).send("Isn't 4000 events enough for you?");
			return;
		}
		events[req.authuser].push({
			id:nextid++,
			sheet:sheet,
			text:text,
			date:date,
			type:type
		});
		events[req.authuser].sort(function(a, b) { return a.date - b.date; });
		persist.setItemSync("events",events);
		persist.setItemSync("nextid",nextid);
		res.status(200).end();
	});
};