// Express is used for HTTP and URL routing
const express = require('express');

const path = require('path');
const PORT = process.env.PORT || 5000;

// Library to generate unique identifiers 
const { v4: uuidv4 } = require('uuid');
const uuid = require('uuid');

// We need the crypto library for sha512 encryption of passwords in the database
var crypto = require("crypto");

// psql driver 
const { Pool } = require('pg');

const app = express();
const http = require('http');
const server = http.createServer(app);
const io = require('socket.io')(server, {
  cors: {
    origin: "*"
  }
});

/*
Connects to database on deployed app. 
*/
const pool = new Pool({
  connectionString: process.env.DATABASE_URL,
  ssl: {
    rejectUnauthorized: false
  }
});


app.use(express.json())
app.use(express.urlencoded({
  extended: true
}))

users = {}

io.on("connection", (socket) => {
  socket.emit("hello", {"hello": "world"});
  socket.on('disconnect', () => {
    console.log('user disconnected');
	delete users[socket.id];
	socket.broadcast.emit('users', getUsers());
  });
  socket.on("hello", (arg) => {
    console.log("hello" + arg); // world
  });
  socket.on("message", (arg) => {
    socket.broadcast.emit('message', arg);
	saveMessage(users[socket.id], arg);
  });
  socket.on("privateMessage", (msg, to) => {
	var socketID = [];
	for (user in users){
		console.log(user);
		if (users[user].toString() == to.toString()){
		}
	}
	console.log("Private message to " + to + " " + socketID[0]);
    io.to(socketID[0]).emit('privateMessage', users[socket.id], msg);
	saveMessage(users[socket.id], users[socketID[0]], msg);
  });
  socket.on("login", (data) => {
	  data = data.split(",");
	  var username = data[0];
	  var pw = data[1];
	  pool.query(`SELECT username,password FROM accounts where username='${username}'`, (err, result) => {
		  if (err){
			  error('A critical database error occured while user was attempting to login');
			  socket.emit('logInError', 'A critical database error occured while user was attempting to login');
		  }
		  else if (result.rowCount == 0){
			  socket.emit('logInError', `Invalid credentials`);
		  }
		  else if (result.rows[0].username == username && result.rows[0].password == sha512(pw)){
				var uhash = sha512(username);
				socket.emit('users', getUsers());
				users[socket.id] = username;
				socket.broadcast.emit('users', getUsers());
				socket.emit('loggedIn', `${username}`);
				getMessages();
				log('User logged in');
		  }
		  else {
			  socket.emit('logInError', `Invalid credentials`);
		  }
	  });
  });
  socket.on("register", (data) => {
	  data = data.split(",");
	  var id = uuidv4();
	  var username = data[0];
	  var pw = data[1];
	  pool.query(`SELECT * FROM accounts where username='${username}'`, (err, result) => {
		  if (err) {
			error(err);
			error('A critical database error occured while user was attempting to create an account');
			socket.emit('registerError', 'A critical database error occured while user was attempting to create an account');
		  }
		  else {
			  if (result.rowCount == 0){
					var pwhash = sha512(pw);
					var uhash = sha512(username);
					pool.query(`insert into accounts values('${id}', '${username}', '${pwhash}')`, (err, result) => {
						console.log(err);
						console.log(result);
						if (err){
							socket.emit('registerError', 'A critical database error occured while user was attempting to create an account');
						}
						else {
							socket.username = username;
							socket.emit('registered', `${username}`);
							log(`${username} registered with ${id}`);
						}
					});
			  }
			  else {
				  socket.emit('registerError', `Username already exists`);
			  }
		  }
	  })
	 
  });
  function getMessages(){
		pool.query(`SELECT MESSAGE FROM message where TO_USER='all'`, (err, result) => {
			//console.log(result.rows);
			console.log("GETTING MESSAGES");
			socket.emit('messages', result.rows);
			
		});
	}
});

/**
 * hash password with sha512.
 * @function
 * @param {string} password - List of required fields.
 * @param {string} salt - Data to be validated.
 */
var sha512 = function(password, salt = "messaging"){
    var hash = crypto.createHmac('sha512', salt); /** Hashing algorithm sha512 */
    hash.update(password);
    var value = hash.digest('hex');
    return value;
};

server.listen(PORT, function(){ log("Listening on port " + PORT); })

function getUsers(){
	u = [];
	for (user in users){
		u.push(users[user]);
	}
	return u;
}

function saveMessage(from, msg){
	pool.query(`insert into message values('${from}', 'all', '${msg}')`, (err, result) => {});
}

function savePrivateMessage(from, to, msg){
	pool.query(`insert into message values('${from}', '${to}', '${msg}')`, (err, result) => {});
}



function log(input){
	console.log(input);
}

function error(input){
	console.error(input);
}