package me.despawningbone.discordbot.command.info;

import java.awt.Color;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.TimeZone;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONTokener;
import org.jsoup.Connection.Response;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;

import me.despawningbone.discordbot.command.Command;
import me.despawningbone.discordbot.command.CommandResult;
import me.despawningbone.discordbot.command.CommandResult.CommandResultType;
import me.despawningbone.discordbot.utils.MiscUtils;
import net.dv8tion.jda.api.EmbedBuilder;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.MessageEmbed;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.User;
import net.dv8tion.jda.api.exceptions.InsufficientPermissionException;

public class CityInfo extends Command {
	public CityInfo() {
		this.alias = Arrays.asList("ci", "weather");
		this.desc = "Search for info about a city!"; //"Search for info about the city the address is in!";
		this.usage = "<address/search words>";
		this.examples = Arrays.asList("hong kong", "tokyo");  //"HK", "akihabara");
		
		String[] countryCodes = Locale.getISOCountries();
		for (String cc : countryCodes) {
		    countries.put(new Locale("", cc).getDisplayCountry(), cc.toUpperCase());
		}
	} 
	
	private HashMap<String, String> countries = new HashMap<String, String>();
	
	NumberFormat formatter = new DecimalFormat("#0.00");
	
	@Override
	public CommandResult execute(TextChannel channel, User author, Message msg, String[] args) {
		if(args.length < 1) {
			return new CommandResult(CommandResultType.INVALIDARGS, "Please input a city name."); //or a address.");
		 } else {
			channel.sendTyping().queue();
			String sword = String.join(" ", args);
			try {
	        	//www.yahoo.com changed its endpoint - there's no longer a AJAX API for weather info, and the search autocomplete is basically just a prefix search which is way inferior; so we use ca.news.yahoo.com instead
	        	URLConnection sCon = new URL("https://ca.news.yahoo.com/tdv2_fp/api/resource/WeatherLocationService.autocomplete?text=" + URLEncoder.encode(sword, "UTF-8")).openConnection();
	        	sCon.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0");
	        	
	        	JSONArray res = new JSONArray(new JSONTokener(sCon.getInputStream()));
	        	
	        	if(res.length() < 1) return new CommandResult(CommandResultType.NORESULT);
	        	
	        	JSONObject wsearch = res.getJSONObject(0);
	        	String woeid = String.valueOf(wsearch.getInt("woeid"));
	        	String lat = String.valueOf(wsearch.getDouble("lat"));
	        	String lng = String.valueOf(wsearch.getDouble("lon"));
	        	String wQualifiedName = wsearch.getString("qualifiedName");
	        	String countryShort = wsearch.getString("country");
	        	String region = wsearch.getString("state").isEmpty() ? countryShort : wsearch.getString("state");
	        	
	        	//original API no longer returns results, seems like all of the current info is generated server side so do some parsing instead
	        	Document mainCon = Jsoup.connect("https://ca.news.yahoo.com/weather/" + wsearch.getString("country") + "/" + (wsearch.getString("state").isEmpty() ? wsearch.getString("country") : wsearch.getString("state")) + "/" + wsearch.getString("city") + "-" + woeid).get();
	        	int timeOffset = (int) Math.round(Duration.between(Instant.now(), Instant.from(DateTimeFormatter.ofPattern("yyyy M-d, h:m a X").parse(OffsetDateTime.now().getYear() + " " + mainCon.selectFirst("#module-location-heading time").text().replace(".", "").toUpperCase() + " Z"))).getSeconds() / 60.0 / 60.0);
	        	String tempNow = mainCon.selectFirst(".temperature-forecast .celsius").text();
	        	//not sure why ~= doesnt work in jsoup but works in normal browsers
	        	String tempHigh = mainCon.selectFirst("#module-location-heading .arrowUp ~ span[class*=\"celsius_D(b)\"]").text();
	        	String tempLow = mainCon.selectFirst("#module-location-heading .arrowDown ~ span[class*=\"celsius_D(b)\"]").text();
	        	
	        	String humidity = mainCon.selectFirst("#module-weather-details dt:contains(Humidity)").nextElementSibling().text();
	        	String precipitationProb = mainCon.select(".hourlyForecast .precipitation dt").get(1).text();
	        	String visibility = mainCon.selectFirst("#module-weather-details dt:contains(Visibility) ~ dd[class*=\"kilometers_D(b)\"]").text();
	        	String conditionDesc = mainCon.selectFirst("#module-location-heading p").text();
	        	String feelsLike = mainCon.selectFirst("#module-weather-details dt:contains(RealFeel®) ~ dd[class*=\"celsius_D(b)\"]").text();
	        	String uv = mainCon.selectFirst("#module-weather-details dt:contains(UV Index)").nextElementSibling().text();
	        	
	        	String pressure = mainCon.selectFirst("#module-weather-wind-pressure dt:contains(Barometer) ~ dd[class*=\"celsius_D(b)\"]").text();
	        	String[] wind = mainCon.selectFirst("#module-weather-wind-pressure dt:contains(Wind) ~ dd[class*=\"kilometers_D(b)\"]").text().split(" ");
	        	String windSpeed = wind[0] + " " + wind[1];
	        	String windDir = wind[2];
	        	Elements sun = mainCon.select("#module-weather-sun-moon time");
	        	String sunrise = sun.get(0).text();
	        	String sunset = sun.get(1).text();
	        	
	        	String imgUrl = mainCon.selectFirst("picture img").attr("data-wf-src");
	        	
	        	
				EmbedBuilder embedmsg = new EmbedBuilder();
		        embedmsg.setAuthor("Info for " + wQualifiedName, null, null);
		        embedmsg.setColor(new Color(100, 0, 255));
		        embedmsg.addField("Country", countries.containsKey(countryShort) ? MiscUtils.countryNameToUnicode(countries.get(countryShort)) : countryShort, true);
		        embedmsg.addField("Region", region, true);
		        embedmsg.addField("Current time", OffsetDateTime.now(ZoneOffset.ofHours(timeOffset)).format(DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ssa").withLocale(Locale.ENGLISH)).trim() + " (UTC" + (Math.signum(timeOffset) == 1 || Math.signum(timeOffset) == 0 ? "+" + timeOffset : timeOffset) + ")", true);
		        
	        	embedmsg.addField("Temperature", tempNow + "°C (↑" + tempHigh + "C | ↓" + tempLow + "C)", true);
		        embedmsg.addField("Feels Like", feelsLike + "C", true);
		        embedmsg.addField("Humidity", humidity + " (Chance of rain: " + precipitationProb + ")", true);
		        embedmsg.addField("Atmospheric pressure", pressure, true);
		        embedmsg.addField("Wind speed", windSpeed, true);
		        embedmsg.addField("Wind direction", windDir, true);
		        embedmsg.addField("UV index", uv, true);
		        embedmsg.addField("Sunrise", sunrise, true);
		        embedmsg.addField("Sunset", sunset, true);
		        embedmsg.addField("Visibility", visibility + " (" + conditionDesc + ")", true);
		        embedmsg.setThumbnail(imgUrl);
		        
		        embedmsg.addField("Latitude", lat, true);
		        embedmsg.addField("Longitude", lng, true);
		        //no weather info update time anymore, just credit accuweather ig
		        embedmsg.setFooter("Powered by Accuweather / Yahoo", null);
		        try {
		        	MessageEmbed fmsg = embedmsg.build();
		        	channel.sendMessageEmbeds(fmsg).queue();
		        } catch(InsufficientPermissionException e2) {
		        	return new CommandResult(CommandResultType.FAILURE, "Unfortunately, the bot is missing the permission `MESSAGE_EMBED_LINKS` which is required for this command to work.");
		        }
		        return new CommandResult(CommandResultType.SUCCESS);
			} catch (Exception e) {
				return new CommandResult(CommandResultType.ERROR, ExceptionUtils.getStackTrace(e));
			}
		 }
	}
}
