const osu = require("osu-packet"),
	  User = require("./User.js"),
	  uuid = require("./util/shortUUID.js"),
	  ahttp = require("./util/AsyncHttpRequest.js"),
	  consoleHelper = require("../consoleHelper.js"),
	  // Packets
	  getUserByUsername = require("./util/getUserByUsername.js"),
	  getUserByToken = require("./util/getUserByToken.js"),
	  countryHelper = require("./countryHelper.js"),
	  loginHelper = require("./loginHelper.js"),
	  Logout = require("./Packets/Logout.js"),
	  Streams = require("./Streams.js"),
	  UserPresenceBundle = require("./Packets/UserPresenceBundle.js"),
	  UserPresence = require("./Packets/UserPresence.js"),
	  StatusUpdate = require("./Packets/StatusUpdate.js");

module.exports = async function(req, res, loginInfo) {
	// Get time at the start of login
	const loginStartTime = Date.now(),
		  isTourneyClient = loginInfo.osuversion.includes("tourney");

	// Check login
	const loginCheck = await loginHelper.checkLogin(loginInfo);
	if (loginCheck != null) {
		res.removeHeader('X-Powered-By');
		res.removeHeader('Date');
		res.writeHead(200, loginCheck[1]);
		return res.end(loginCheck[0]);
	}

	// Get users IP for getting location
	// Get cloudflare requestee IP first
	let requestIP = req.get("cf-connecting-ip");

	// Get IP of requestee since we are probably behind a reverse proxy
	if (requestIP == null)
		requestIP = req.get("X-Real-IP");

	// Just get the requestee IP (we are not behind a reverse proxy)
	if (requestIP == null)
		requestIP = req.remote_addr;

	// Make sure requestIP is never null
	if (requestIP == null)
		requestIP = "";

	
	let userLocationData = [], userLocation;
	// Check if it is a local or null IP
	if (requestIP.includes("192.168.") || requestIP.includes("127.0.") || requestIP == "") {
		// Set location to null island
		userLocationData.country = "XX";
		userLocation = [0, 0];
	} else {
		// Get user's location using zxq
		userLocationData = await ahttp(`http://ip.zxq.co/${requestIP}`, "json");
		userLocation = userLocationData.loc.split(",");
	}

	// Get information about the user from the database
	const userDB = await global.DatabaseHelper.query("SELECT id FROM users_info WHERE username = ? LIMIT 1", [loginInfo.username]);

	// Create a token for the client
	const newClientToken = uuid();

	// Make sure user is not already connected, kick off if so.
	const connectedUser = getUserByUsername(loginInfo.username);
	if (connectedUser != null && !isTourneyClient && !connectedUser.isTourneyUser) {
		Logout(connectedUser);
	}

	// Retreive the newly created user
	const NewUser = global.users.add(newClientToken, new User(userDB.id, loginInfo.username, newClientToken));
	// Set tourney client flag
	NewUser.isTourneyUser = isTourneyClient;

	// Get user's data from the database
	NewUser.updateUserInfo();

	try {
		// Save the user's location to their class for later use
		NewUser.location[0] = parseFloat(userLocation[0]);
		NewUser.location[1] = parseFloat(userLocation[1]);

		// Save the country id for the same reason as above
		NewUser.countryID = countryHelper.getCountryID(userLocationData.country);

		// We're ready to start putting together a login packet
		// Create an osu! Packet writer
		let osuPacketWriter = new osu.Bancho.Writer;

		// The reply id is the user's id in any other case than an error in which case negative numbers are used
		osuPacketWriter.LoginReply(NewUser.id);
		// Current bancho protocol version. Defined in Binato.js
		osuPacketWriter.ProtocolNegotiation(global.protocolVersion);
		// Permission level 4 is osu!supporter
		osuPacketWriter.LoginPermissions(4);

		// After sending the user their friends list send them the online users
		UserPresenceBundle(NewUser);

		// Set title screen image
		//osuPacketWriter.TitleUpdate("http://puu.sh/jh7t7/20c04029ad.png|https://osu.ppy.sh/news/123912240253");

		// Add user panel data packets
		UserPresence(NewUser, NewUser.id);
		StatusUpdate(NewUser, NewUser.id);

		// peppy pls, why
		osuPacketWriter.ChannelListingComplete();

		// Add user to #osu
		osuPacketWriter.ChannelJoinSuccess("#osu");
		if (!Streams.isUserInStream("#osu", NewUser.uuid))
			Streams.addUserToStream("#osu", NewUser.uuid);

		// List all channels out to the client
		for (let i = 0; i < global.channels.length; i++) {
			osuPacketWriter.ChannelAvailable({
				channelName: global.channels[i].channelName,
				channelTopic: global.channels[i].channelTopic,
				channelUserCount: global.channels[i].channelUserCount
			});
		}

		// Construct user's friends list
		const userFriends = await global.DatabaseHelper.query("SELECT friendsWith FROM friends WHERE user = ?", [NewUser.id]);
		let friendsArray = [];
		for (let i = 0; i < userFriends.length; i++) {
			friendsArray.push(userFriends[i].friendsWith);
		}
		// Send user's friends list
		osuPacketWriter.FriendsList(friendsArray);

		osuPacketWriter.Announce(`Welcome back ${loginInfo.username}!`);

		global.DatabaseHelper.query("UPDATE osu_info SET value = ? WHERE name = 'online_now'", [global.users.getLength() - 1]);

		res.removeHeader('X-Powered-By');
		res.removeHeader('Date');
		// Complete login
		res.writeHead(200, {
			"cho-token": NewUser.uuid,
			"Connection": "keep-alive",
			"Keep-Alive": "timeout=5, max=100",
		});
		res.end(osuPacketWriter.toBuffer, () => {
			consoleHelper.printBancho(`User login finished, took ${Date.now() - loginStartTime}ms. [User: ${loginInfo.username}]`);
		});
	} catch (err) {
		console.error(err);
	}
}