package de.yannicpunktdee.yoshibot.utils;

import lombok.Getter;
import net.dv8tion.jda.api.EmbedBuilder;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.StreamSupport;

public class StatusProvider implements Provider {
	
	private static final ScheduledExecutorService statusScheduler = Executors.newScheduledThreadPool(1);
	
	private int lastPlayersOnline = -1;
	
	@Getter
	private final String ip;
	
	private final String desc;
	
	private LocalDateTime timestampLastPlayerOnline;
	
	private final ProcessBuilder mcstatus = new ProcessBuilder();
	
	
	public StatusProvider(String desc, int secondsPerTime, String ip, LocalDateTime timestamp) {
		this.timestampLastPlayerOnline = timestamp;
		this.desc                      = desc;
		this.ip                        = ip;
		this.mcstatus.command(Resources.getPath_to_mcstatus(), ip, "json");
		
		statusScheduler.scheduleAtFixedRate(this::updateStatusMessage, 0, secondsPerTime,
											TimeUnit.SECONDS);
	}
	
	@SuppressWarnings("unchecked")
	public void updateStatusMessage() {
		EmbedBuilder eb = new EmbedBuilder();
		eb.setTitle(desc);
		eb.addField("IP", ip, false);
		
		try {
			Map<String, Object> serverInfo = getPlayersOnline();
			if ((boolean) serverInfo.get("online")) {
				if ((boolean) serverInfo.get("Server starting")) {
					eb.addField("Server still Starting", "True", false);
				} else {
					int newPlayersOnline = (int) serverInfo.get("playerCount");
					eb.addField("Version", (String) serverInfo.get("version"), true);
					eb.addField("MOTD", (String) serverInfo.get("motd"), true);
					eb.addField("Spieler online", newPlayersOnline + " / " + serverInfo.get("playerMax"), false);
					if (newPlayersOnline == 0) {
						if (lastPlayersOnline > newPlayersOnline) {
							timestampLastPlayerOnline = LocalDateTime.now();
							StatusProviderFactory.updateTimestamp(this, timestampLastPlayerOnline);
						}
						eb.addField("Zuletzt gesehen",
									StatusProviderFactory.TIME_FORMATTER.format(timestampLastPlayerOnline), false);
					} else {
						eb.addField("Spieler:", String.join(", ", (List<String>) serverInfo.get("playerNames")), false);
						
					}
					lastPlayersOnline = newPlayersOnline;
				}
			} else {
				eb.addField("Offline", "", false);
			}
			
			StatusProviderFactory.updateStatus(this, eb.build());
		} catch (IOException e) {
			Logger.logError(e.toString());
		}
	}
	
	private Map<String, Object> getPlayersOnline() throws IOException {
		
		Process process = mcstatus.start();
		
		try {
			process.waitFor();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		if (process.exitValue() != 0) {
			Logger.logError("MCStatus on IP " + ip + " exited with errorcode " + process.exitValue());
		}
		
		String output = new BufferedReader(new InputStreamReader(process.getInputStream())).lines()
				.collect(Collectors.joining());
		
		Map<String, Object> result = new HashMap<>();
		
		if (output.startsWith("The server did not respond to the query protocol.")) {
			result.put("online", false);
			return result;
		}
		JSONObject obj = new JSONObject(output);
		
		result.put("online", obj.getBoolean("online"));
		
		if (!obj.getBoolean("online")) {
			return result;
		}
		for (String key : new String[]{"player_count", "player_max", "players", "motd", "version"}) {
			if (!obj.has(key)) {
				result.put("Server starting", true);
				return result;
			}
		}
		result.put("Server starting", false);
		
		result.put("playerCount", obj.getInt("player_count"));
		result.put("playerMax", obj.getInt("player_max"));
		result.put("playerNames", StreamSupport.stream(obj.getJSONArray("players").spliterator(), false)
				.map(jsonobj -> ((JSONObject) jsonobj).getString("name")).sorted().collect(Collectors.toList()));
		result.put("motd", obj.getString("motd"));
		result.put("version", obj.getString("version"));
		
		return result;
	}
	
	@Override
	public void onStop() {
		StatusProviderFactory.updateTimestamp(this, timestampLastPlayerOnline);
	}
}
