package cn.citycraft.AuthMe.process.login;

import java.util.Date;
import java.util.List;

import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.scheduler.BukkitTask;

import cn.citycraft.AuthMe.AuthMe;
import cn.citycraft.AuthMe.ConsoleLogger;
import cn.citycraft.AuthMe.Utils;
import cn.citycraft.AuthMe.cache.auth.PlayerAuth;
import cn.citycraft.AuthMe.cache.auth.PlayerCache;
import cn.citycraft.AuthMe.cache.limbo.LimboCache;
import cn.citycraft.AuthMe.datasource.DataSource;
import cn.citycraft.AuthMe.listener.AuthMePlayerListener;
import cn.citycraft.AuthMe.security.PasswordSecurity;
import cn.citycraft.AuthMe.security.RandomString;
import cn.citycraft.AuthMe.settings.Messages;
import cn.citycraft.AuthMe.settings.Settings;
import cn.citycraft.AuthMe.task.MessageTask;
import fr.xephi.authme.events.AuthMeAsyncPreLoginEvent;

public class AsyncronousLogin {

	private static RandomString rdm = new RandomString(Settings.captchaLength);
	private final DataSource database;
	private final Messages m = Messages.getInstance();
	private final AuthMe plugin;
	protected boolean forceLogin;
	protected String name;
	protected String password;
	protected Player player;
	protected String realName;

	public AsyncronousLogin(final Player player, final String password, final boolean forceLogin, final AuthMe plugin, final DataSource data) {
		this.player = player;
		this.password = password;
		name = player.getName().toLowerCase();
		realName = player.getName();
		this.forceLogin = forceLogin;
		this.plugin = plugin;
		this.database = data;
	}

	public void displayOtherAccounts(final PlayerAuth auth, final Player p) {
		if (!Settings.displayOtherAccounts) {
			return;
		}
		if (auth == null) {
			return;
		}
		final List<String> auths = this.database.getAllAuthsByName(auth);
		// List<String> uuidlist =
		// plugin.otherAccounts.getAllPlayersByUUID(player);
		if (auths.isEmpty()) {
			return;
		}
		if (auths.size() == 1) {
			return;
		}
		final StringBuilder message = new StringBuilder("[AuthMe] ");
		// String uuidaccounts =
		// "[AuthMe] PlayerNames has %size% links to this UUID : ";
		int i = 0;
		for (final String account : auths) {
			i++;
			message.append(account);
			if (i != auths.size()) {
				message.append(", ");
			} else {
				message.append(".");
			}
		}
		/*
		 * TODO: Active uuid system i = 0; for (String account : uuidlist) {
		 * i++; uuidaccounts = uuidaccounts + account; if (i != auths.size()) {
		 * uuidaccounts = uuidaccounts + ", "; } else { uuidaccounts =
		 * uuidaccounts + "."; } }
		 */
		for (final Player player : Utils.getOnlinePlayers()) {
			if (plugin.authmePermissible(player, "authme.seeOtherAccounts")) {
				player.sendMessage("[AuthMe] 当前玩家 " + auth.getNickname() + " 拥有 " + auths.size() + " 个账户!");
				player.sendMessage(message.toString());
				// player.sendMessage(uuidaccounts.replace("%size%",
				// ""+uuidlist.size()));
			}
		}
	}

	public void process() {
		final PlayerAuth pAuth = preAuth();
		if (pAuth == null || needsCaptcha()) {
			return;
		}

		final String hash = pAuth.getHash();
		final String email = pAuth.getEmail();
		boolean passwordVerified = true;
		if (!forceLogin) {
			try {
				passwordVerified = PasswordSecurity.comparePasswordWithHash(password, hash, realName);
			} catch (final Exception ex) {
				ConsoleLogger.showError(ex.getMessage());
				m.send(player, "error");
				return;
			}
		}
		if (passwordVerified && player.isOnline()) {
			final PlayerAuth auth = new PlayerAuth(name, hash, getIP(), new Date().getTime(), email, realName);
			database.updateSession(auth);

			if (Settings.useCaptcha) {
				if (plugin.captcha.containsKey(name)) {
					plugin.captcha.remove(name);
				}
				if (plugin.cap.containsKey(name)) {
					plugin.cap.remove(name);
				}
			}

			player.setNoDamageTicks(0);
			if (!forceLogin) {
				m.send(player, "login");
			}

			displayOtherAccounts(auth, player);

			if (Settings.recallEmail) {
				if (email == null || email.isEmpty() || email.equalsIgnoreCase("your@email.com")) {
					m.send(player, "add_email");
				}
			}

			if (!Settings.noConsoleSpam) {
				ConsoleLogger.info(realName + " 登陆成功!");
			}

			// makes player isLoggedin via API
			PlayerCache.getInstance().addPlayer(auth);
			database.setLogged(name);
			plugin.otherAccounts.addPlayer(player.getName());

			// As the scheduling executes the Task most likely after the current
			// task, we schedule it in the end
			// so that we can be sure, and have not to care if it might be
			// processed in other order.
			final ProcessSyncronousPlayerLogin syncronousPlayerLogin = new ProcessSyncronousPlayerLogin(player, plugin, database);
			if (syncronousPlayerLogin.getLimbo() != null) {
				if (syncronousPlayerLogin.getLimbo().getTimeoutTaskId() != null) {
					syncronousPlayerLogin.getLimbo().getTimeoutTaskId().cancel();
				}
				if (syncronousPlayerLogin.getLimbo().getMessageTaskId() != null) {
					syncronousPlayerLogin.getLimbo().getMessageTaskId().cancel();
				}
			}
			Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, syncronousPlayerLogin);
		} else if (player.isOnline()) {
			if (!Settings.noConsoleSpam) {
				ConsoleLogger.info(realName + " 输入了错误的密码");
			}
			if (Settings.isKickOnWrongPasswordEnabled) {
				Bukkit.getScheduler().scheduleSyncDelayedTask(plugin, new Runnable() {

					@Override
					public void run() {
						if (AuthMePlayerListener.gameMode != null && AuthMePlayerListener.gameMode.containsKey(name)) {
							player.setGameMode(AuthMePlayerListener.gameMode.get(name));
						}
						player.kickPlayer(m.send("wrong_pwd")[0]);
					}
				});
			} else {
				m.send(player, "wrong_pwd");
			}
		} else {
			ConsoleLogger.showError("玩家 " + name + " 在进行登陆流程时下线了, 已中断... ");
		}
	}

	protected String getIP() {
		return plugin.getIP(player);
	}

	protected boolean needsCaptcha() {
		if (Settings.useCaptcha) {
			if (!plugin.captcha.containsKey(name)) {
				plugin.captcha.putIfAbsent(name, 1);
			} else {
				final int i = plugin.captcha.get(name) + 1;
				plugin.captcha.remove(name);
				plugin.captcha.putIfAbsent(name, i);
			}
			if (plugin.captcha.containsKey(name) && plugin.captcha.get(name) >= Settings.maxLoginTry) {
				plugin.cap.put(name, rdm.nextString());
				for (final String s : m.send("usage_captcha")) {
					player.sendMessage(s.replace("THE_CAPTCHA", plugin.cap.get(name)).replace("<theCaptcha>", plugin.cap.get(name)));
				}
				return true;
			} else if (plugin.captcha.containsKey(name) && plugin.captcha.get(name) >= Settings.maxLoginTry) {
				plugin.captcha.remove(name);
				plugin.cap.remove(name);
			}
		}
		return false;
	}

	/**
	 * Checks the precondition for authentication (like user known) and returns
	 * the playerAuth-State
	 */
	protected PlayerAuth preAuth() {
		if (PlayerCache.getInstance().isAuthenticated(name)) {
			m.send(player, "logged_in");
			return null;
		}
		if (!database.isAuthAvailable(name)) {
			m.send(player, "user_unknown");
			if (LimboCache.getInstance().hasLimboPlayer(name)) {
				LimboCache.getInstance().getLimboPlayer(name).getMessageTaskId().cancel();
				String[] msg;
				if (Settings.emailRegistration) {
					msg = m.send("reg_email_msg");
				} else {
					msg = m.send("reg_msg");
				}
				final BukkitTask msgT = Bukkit.getScheduler().runTaskAsynchronously(plugin, new MessageTask(plugin, name, msg, Settings.getWarnMessageInterval));
				LimboCache.getInstance().getLimboPlayer(name).setMessageTaskId(msgT);
			}
			return null;
		}
		if (Settings.getMaxLoginPerIp > 0 && !plugin.authmePermissible(player, "authme.allow2accounts") && !getIP().equalsIgnoreCase("127.0.0.1") && !getIP().equalsIgnoreCase("localhost")) {
			if (plugin.isLoggedIp(name, getIP())) {
				m.send(player, "logged_in");
				return null;
			}
		}
		final PlayerAuth pAuth = database.getAuth(name);
		if (pAuth == null) {
			m.send(player, "user_unknown");
			return null;
		}
		if (!Settings.getMySQLColumnGroup.isEmpty() && pAuth.getGroupId() == Settings.getNonActivatedGroup) {
			m.send(player, "vb_nonActiv");
			return null;
		}
		final AuthMeAsyncPreLoginEvent event = new AuthMeAsyncPreLoginEvent(player);
		Bukkit.getServer().getPluginManager().callEvent(event);
		if (!event.canLogin()) {
			return null;
		}
		return pAuth;
	}
}
