package com.tiedup.remake.events.system;

import com.tiedup.remake.bounty.Bounty;
import com.tiedup.remake.bounty.BountyManager;
import com.tiedup.remake.core.SettingsAccessor;
import com.tiedup.remake.core.TiedUpMod;
import com.tiedup.remake.state.IBondageState;
import com.tiedup.remake.state.PlayerBindState;
import com.tiedup.remake.state.PlayerCaptorManager;
import com.tiedup.remake.util.KidnappedHelper;
import java.util.List;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraftforge.event.TickEvent;
import net.minecraftforge.event.entity.player.PlayerEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;

/**
 * Handles bounty delivery detection and player login events.
 *
 *
 * Detects when:
 * - A hunter brings a captive near the bounty client
 * - A player logs in (to return expired bounty rewards)
 */
@Mod.EventBusSubscriber(
    modid = TiedUpMod.MOD_ID,
    bus = Mod.EventBusSubscriber.Bus.FORGE
)
public class BountyDeliveryHandler {

    // Check every 30 ticks (1.5 seconds) - optimized for performance
    private static final int CHECK_INTERVAL = 30;
    private static int tickCounter = 0;

    /**
     * Periodically check for bounty deliveries.
     */
    @SubscribeEvent
    public static void onServerTick(TickEvent.ServerTickEvent event) {
        if (event.phase != TickEvent.Phase.END) return;

        tickCounter++;
        if (tickCounter < CHECK_INTERVAL) return;
        tickCounter = 0;

        // Check all players
        for (ServerPlayer player : event
            .getServer()
            .getPlayerList()
            .getPlayers()) {
            checkBountyDelivery(player);
        }
    }

    /**
     * Check if this player can deliver any captives to nearby bounty clients.
     */
    private static void checkBountyDelivery(ServerPlayer hunter) {
        // Get hunter's captor manager
        PlayerBindState hunterState = PlayerBindState.getInstance(hunter);
        if (hunterState == null) return;

        PlayerCaptorManager captorManager = hunterState.getCaptorManager();
        if (captorManager == null || !captorManager.hasCaptives()) return;

        // Get bounty manager
        BountyManager bountyManager = BountyManager.get(hunter.serverLevel());
        List<Bounty> bounties = bountyManager.getBounties(hunter.serverLevel());
        if (bounties.isEmpty()) return;

        // Get delivery radius
        int radius = SettingsAccessor.getBountyDeliveryRadius(
            hunter.serverLevel().getGameRules()
        );
        double radiusSq = (double) radius * radius;

        // Find nearby players using pre-maintained player list (faster than AABB query)
        List<ServerPlayer> nearbyPlayers = new java.util.ArrayList<>();
        for (Player p : hunter.level().players()) {
            if (
                p != hunter &&
                p instanceof ServerPlayer sp &&
                hunter.distanceToSqr(p) <= radiusSq
            ) {
                nearbyPlayers.add(sp);
            }
        }

        // Pre-filter captives: only ServerPlayers that are tied (O(m) instead of O(n*m))
        List<ServerPlayer> validCaptives = new java.util.ArrayList<>();
        for (IBondageState captive : captorManager.getCaptives()) {
            LivingEntity captiveEntity = captive.asLivingEntity();
            if (captiveEntity == null) continue;
            if (
                !(captiveEntity instanceof ServerPlayer captivePlayer)
            ) continue;
            IBondageState captiveState = KidnappedHelper.getKidnappedState(
                captivePlayer
            );
            if (captiveState == null || !captiveState.isTiedUp()) continue;
            validCaptives.add(captivePlayer);
        }

        if (validCaptives.isEmpty()) return;

        // Check each nearby player for potential delivery
        for (ServerPlayer potentialClient : nearbyPlayers) {
            for (ServerPlayer captivePlayer : validCaptives) {
                // Captive must be near the client
                if (
                    captivePlayer.distanceTo(potentialClient) > radius
                ) continue;

                // Try to deliver
                boolean delivered = bountyManager.tryDeliverCaptive(
                    hunter,
                    potentialClient,
                    captivePlayer
                );

                if (delivered) {
                    TiedUpMod.LOGGER.info(
                        "[BOUNTY] Delivery detected: {} delivered {} to {}",
                        hunter.getName().getString(),
                        captivePlayer.getName().getString(),
                        potentialClient.getName().getString()
                    );
                }
            }
        }
    }

    /**
     * Handle player login - return pending bounty rewards.
     */
    @SubscribeEvent
    public static void onPlayerLogin(PlayerEvent.PlayerLoggedInEvent event) {
        if (event.getEntity() instanceof ServerPlayer player) {
            BountyManager manager = BountyManager.get(player.serverLevel());
            manager.onPlayerJoin(player);
        }
    }
}
