package wtf.sten.vault;

import com.google.gson.JsonObject;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.screens.Screen;
import net.minecraft.client.gui.screens.TitleScreen;
import net.minecraft.client.gui.screens.ConnectScreen;
import net.minecraft.client.multiplayer.ClientPacketListener;
import net.minecraft.client.multiplayer.ServerData;
import net.minecraft.client.multiplayer.resolver.ServerAddress;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.world.entity.player.Input;

import java.util.Locale;
import java.util.concurrent.CompletableFuture;

public final class Remote {
	private static String heldDir = "";
	private static long heldUntil;

	private Remote() {
	}

	public static void tick(Minecraft mc) {
		if (!heldDir.isEmpty() && System.currentTimeMillis() >= heldUntil) {
			heldDir = "";
		}
	}

	public static Input heldInput() {
		if (heldDir.isEmpty() || System.currentTimeMillis() >= heldUntil) return null;
		String dir = heldDir;
		return new Input(
			"forward".equals(dir),
			"back".equals(dir),
			"left".equals(dir),
			"right".equals(dir),
			"jump".equals(dir),
			"sneak".equals(dir),
			"sprint".equals(dir)
		);
	}

	public static void apply(JsonObject cmd) {
		if (cmd == null || !VaultConfig.get().remoteControl) return;
		try {
			Minecraft mc = Minecraft.getInstance();
			String kind = str(cmd, "kind");
			switch (kind) {
				case "chat" -> chat(mc, str(cmd, "text"));
				case "move" -> move(mc, str(cmd, "dir"), num(cmd, "ms", 400));
				case "look" -> look(mc, (float) num(cmd, "yaw", 0), (float) num(cmd, "pitch", 0));
				case "screenshot" -> Shot.capture(mc, "manual");
				case "switch" -> switchNow(str(cmd, "uid"));
				case "join" -> join(mc, str(cmd, "host"), str(cmd, "proxy"));
				case "proxy" -> setProxy(str(cmd, "proxy"));
				case "disconnect" -> disconnect(mc);
				case "stop" -> stop();
				default -> {
				}
			}
		} catch (Exception ex) {
			StenNet.status = ex.getMessage() == null ? "Remote command failed" : ex.getMessage();
		}
	}

	private static void chat(Minecraft mc, String text) {
		if (text == null || text.isBlank()) return;
		ClientPacketListener conn = mc.getConnection();
		if (conn == null && mc.player != null) conn = mc.player.connection;
		if (conn == null) {
			StenNet.status = "Join a server first";
			return;
		}
		String trimmed = text.trim();
		if (trimmed.length() > 256) trimmed = trimmed.substring(0, 256);
		if (trimmed.startsWith("/")) conn.sendCommand(trimmed.substring(1));
		else conn.sendChat(trimmed);
	}

	private static void move(Minecraft mc, String dir, long ms) {
		if (mc.player == null) {
			StenNet.status = "Not in a world";
			return;
		}
		String next = dir == null ? "" : dir.toLowerCase(Locale.ROOT);
		if (next.isEmpty() || "stop".equals(next)) {
			stop();
			return;
		}
		heldDir = next;
		heldUntil = System.currentTimeMillis() + Math.min(4000, Math.max(50, ms));
	}

	private static void look(Minecraft mc, float yaw, float pitch) {
		LocalPlayer player = mc.player;
		if (player == null) return;
		player.setYRot(player.getYRot() + yaw);
		player.setXRot(Math.max(-90, Math.min(90, player.getXRot() + pitch)));
	}

	static String normalizeHost(String raw) {
		if (raw == null) return "";
		String host = raw.trim();
		int proto = host.indexOf("://");
		if (proto >= 0) host = host.substring(proto + 3);
		int cut = -1;
		for (int i = 0; i < host.length(); i++) {
			char c = host.charAt(i);
			if (c == '/' || c == '?' || c == '#') {
				cut = i;
				break;
			}
		}
		if (cut >= 0) host = host.substring(0, cut);
		int at = host.lastIndexOf('@');
		if (at >= 0) host = host.substring(at + 1);
		if (host.endsWith(".")) host = host.substring(0, host.length() - 1);
		return host.trim();
	}

	private static void join(Minecraft mc, String host, String proxy) {
		String target = normalizeHost(host);
		if (target.isEmpty() || !ServerAddress.isValidAddress(target)) {
			StenNet.status = "Invalid server address";
			return;
		}
		if (proxy != null && !proxy.isBlank()) setProxy(proxy);
		ServerAddress address = ServerAddress.parseString(target);
		ServerData data = new ServerData("sten.wtf", target, ServerData.Type.OTHER);
		mc.execute(() -> {
			try {
				boolean inWorld = mc.level != null;
				if (inWorld) mc.disconnect(new TitleScreen(), false);
				Runnable connect = () -> {
					try {
						Screen parent = mc.gui.screen();
						if (parent == null) parent = new TitleScreen();
						ConnectScreen.startConnecting(parent, mc, address, data, false, null);
					} catch (Exception ex) {
						StenNet.status = ex.getMessage() == null ? "Join failed" : ex.getMessage();
					}
				};
				if (inWorld) mc.execute(connect);
				else connect.run();
			} catch (Exception ex) {
				StenNet.status = ex.getMessage() == null ? "Join failed" : ex.getMessage();
			}
		});
	}

	private static void setProxy(String raw) {
		VaultConfig cfg = VaultConfig.get();
		String next = raw == null ? "" : raw.trim();
		if (!next.isEmpty() && Proxy.parse(next) == null) {
			StenNet.status = "Invalid SOCKS5 proxy";
			return;
		}
		cfg.proxy = next;
		cfg.save();
		StenNet.status = next.isEmpty() ? "Direct connection" : "Proxy " + next;
	}

	private static void disconnect(Minecraft mc) {
		if (mc.level == null) return;
		mc.execute(() -> mc.disconnect(new TitleScreen(), false));
	}

	private static void switchNow(String uid) {
		if (uid == null || uid.isBlank()) return;
		StenNet.status = "Remote switch…";
		CompletableFuture.runAsync(() -> {
			try {
				JsonObject minted = StenNet.switchAccount(uid);
				Minecraft.getInstance().execute(() -> {
					try {
						SessionSwap.apply(minted);
					} catch (Exception ex) {
						StenNet.status = ex.getMessage();
					}
				});
			} catch (Exception ex) {
				StenNet.status = ex.getMessage() == null ? "Switch failed" : ex.getMessage();
			}
		});
	}

	private static void stop() {
		heldDir = "";
	}

	private static String str(JsonObject obj, String key) {
		if (obj == null || !obj.has(key) || obj.get(key).isJsonNull()) return "";
		try {
			return obj.get(key).getAsString();
		} catch (Exception ignored) {
			return "";
		}
	}

	private static long num(JsonObject obj, String key, long fallback) {
		if (obj == null || !obj.has(key) || obj.get(key).isJsonNull()) return fallback;
		try {
			return obj.get(key).getAsLong();
		} catch (Exception ignored) {
			return fallback;
		}
	}
}
