package fr.koka99cab.ibelievechickenfly.chickenboat;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
import net.fabricmc.fabric.api.networking.v1.ServerPlayNetworking;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.vehicle.boat.AbstractBoat;

public final class ChickenBoatServerController {
	private static final int INPUT_TIMEOUT_TICKS = 5;

	private static final Map<UUID, ControlState> CONTROL_STATES = new HashMap<>();

	private ChickenBoatServerController() {
	}

	public static void register() {
		ServerPlayNetworking.registerGlobalReceiver(
			ChickenBoatControlPayload.ID,
			(payload, context) -> updateControls(context.player(), payload)
		);
		ServerTickEvents.END_SERVER_TICK.register(ChickenBoatServerController::tick);
	}

	private static void updateControls(ServerPlayer player, ChickenBoatControlPayload payload) {
		UUID playerId = player.getUUID();

		if (getValidBoat(player) == null) {
			CONTROL_STATES.remove(playerId);
			return;
		}

		CONTROL_STATES.computeIfAbsent(playerId, id -> new ControlState()).update(payload.up());
	}

	private static void tick(MinecraftServer server) {
		for (ServerPlayer player : server.getPlayerList().getPlayers()) {
			tickPlayer(player);
		}
	}

	private static void tickPlayer(ServerPlayer player) {
		UUID playerId = player.getUUID();
		AbstractBoat boat = getValidBoat(player);

		if (boat == null) {
			CONTROL_STATES.remove(playerId);
			return;
		}

		ControlState controls = CONTROL_STATES.get(playerId);
		boolean hasFreshInput = controls != null && controls.ticksSinceUpdate <= INPUT_TIMEOUT_TICKS;
		boolean up = hasFreshInput && controls.up;

		ChickenBoatFlightPhysics.apply(boat, up);

		if (controls != null) {
			controls.ticksSinceUpdate++;
		}
	}

	private static AbstractBoat getValidBoat(ServerPlayer player) {
		Entity vehicle = player.getVehicle();

		if (!(vehicle instanceof AbstractBoat boat)) {
			return null;
		}

		if (boat.level() != player.level() || !boat.isAlive() || !boat.hasPassenger(player)) {
			return null;
		}

		return ChickenBoatUtil.hasChickenPassenger(boat) ? boat : null;
	}

	private static final class ControlState {
		private boolean up;
		private int ticksSinceUpdate;

		private void update(boolean up) {
			this.up = up;
			this.ticksSinceUpdate = 0;
		}
	}
}
