package com.tiedup.remake.util.teleport;

import com.tiedup.remake.core.TiedUpMod;
import java.util.function.Function;
import net.minecraft.resources.ResourceKey;
import net.minecraft.server.MinecraftServer;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.portal.PortalInfo;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.util.ITeleporter;
import org.jetbrains.annotations.Nullable;

/**
 * Utility class for teleporting entities across dimensions.
 *
 * <p>Handles the complexity of cross-dimension teleportation in 1.20.1,
 * including proper chunk loading, entity transfer, and rotation preservation.</p>
 *
 * <h2>Features</h2>
 * <ul>
 *   <li>Same-dimension teleportation</li>
 *   <li>Cross-dimension teleportation</li>
 *   <li>Player-specific handling (smooth client sync)</li>
 *   <li>Rotation preservation</li>
 *   <li>Safe position validation</li>
 * </ul>
 *
 * <h2>Original Reference</h2>
 * Based on Original/com/yuti/kidnapmod/util/teleport/TeleportHelper.java
 */
public class TeleportHelper {

    /**
     * Teleport an entity to a position.
     * Handles both same-dimension and cross-dimension teleportation.
     *
     * @param entity The entity to teleport
     * @param position The target position
     * @return The entity after teleportation (may be different instance after dimension change)
     */
    @Nullable
    public static Entity teleportEntity(Entity entity, Position position) {
        if (entity == null || position == null) {
            return entity;
        }

        // Server-side only
        if (entity.level().isClientSide) {
            TiedUpMod.LOGGER.warn(
                "[TeleportHelper] Attempted client-side teleport"
            );
            return entity;
        }

        // Get server and target level
        MinecraftServer server = entity.getServer();
        if (server == null) {
            TiedUpMod.LOGGER.error(
                "[TeleportHelper] No server found for entity"
            );
            return entity;
        }

        ResourceKey<Level> targetDimension = position.getDimension();
        ServerLevel targetLevel = server.getLevel(targetDimension);

        if (targetLevel == null) {
            TiedUpMod.LOGGER.error(
                "[TeleportHelper] Target dimension not found: {}",
                targetDimension
            );
            return entity;
        }

        // Check if same dimension or cross-dimension
        if (entity.level().dimension().equals(targetDimension)) {
            return teleportSameDimension(entity, position);
        } else {
            return teleportCrossDimension(entity, targetLevel, position);
        }
    }

    /**
     * Teleport an entity within the same dimension.
     *
     * @param entity The entity to teleport
     * @param position The target position
     * @return The entity after teleportation
     */
    private static Entity teleportSameDimension(
        Entity entity,
        Position position
    ) {
        if (entity == null || position == null) {
            return entity;
        }

        // For players, use the connection method for smooth sync
        if (entity instanceof ServerPlayer player) {
            player.connection.teleport(
                position.getX(),
                position.getY(),
                position.getZ(),
                position.getYaw(),
                position.getPitch()
            );

            // Reset fall distance to prevent fall damage after teleport
            player.fallDistance = 0;
            player.resetFallDistance();

            TiedUpMod.LOGGER.info(
                "[TeleportHelper] Teleported player {} to {}",
                player.getName().getString(),
                position.toShortString()
            );
        } else {
            // For other entities, set position and rotation directly
            entity.teleportTo(
                position.getX(),
                position.getY(),
                position.getZ()
            );
            entity.setYRot(position.getYaw());
            entity.setXRot(position.getPitch());
            entity.setYHeadRot(position.getYaw());

            // Reset fall distance to prevent fall damage after teleport
            entity.fallDistance = 0;

            TiedUpMod.LOGGER.info(
                "[TeleportHelper] Teleported entity {} to {}",
                entity.getName().getString(),
                position.toShortString()
            );
        }

        return entity;
    }

    /**
     * Teleport an entity across dimensions.
     *
     * @param entity The entity to teleport
     * @param targetLevel The target dimension
     * @param position The target position
     * @return The entity after teleportation (may be different instance)
     */
    @Nullable
    public static Entity teleportCrossDimension(
        Entity entity,
        ServerLevel targetLevel,
        Position position
    ) {
        if (entity == null || targetLevel == null || position == null) {
            return entity;
        }

        // For players, use changeDimension with custom teleporter
        if (entity instanceof ServerPlayer player) {
            return teleportPlayerCrossDimension(player, targetLevel, position);
        }

        // For other entities, use changeDimension
        Entity newEntity = entity.changeDimension(
            targetLevel,
            new PositionTeleporter(position)
        );

        if (newEntity != null) {
            TiedUpMod.LOGGER.info(
                "[TeleportHelper] Cross-dimension teleport: {} to {} in {}",
                entity.getName().getString(),
                position.toShortString(),
                targetLevel.dimension().location()
            );
        } else {
            TiedUpMod.LOGGER.warn(
                "[TeleportHelper] Cross-dimension teleport failed for {}",
                entity.getName().getString()
            );
        }

        return newEntity;
    }

    /**
     * Teleport a player across dimensions.
     *
     * @param player The player to teleport
     * @param targetLevel The target dimension
     * @param position The target position
     * @return The player after teleportation
     */
    public static ServerPlayer teleportPlayerCrossDimension(
        ServerPlayer player,
        ServerLevel targetLevel,
        Position position
    ) {
        if (player == null || targetLevel == null || position == null) {
            return player;
        }

        // Use Forge's teleportTo method for cross-dimension
        player.changeDimension(targetLevel, new PositionTeleporter(position));

        TiedUpMod.LOGGER.info(
            "[TeleportHelper] Cross-dimension player teleport: {} to {} in {}",
            player.getName().getString(),
            position.toShortString(),
            targetLevel.dimension().location()
        );

        return player;
    }

    /**
     * Teleport a living entity to another entity's position.
     *
     * @param entity The entity to teleport
     * @param target The target entity to teleport to
     * @return The entity after teleportation
     */
    @Nullable
    public static Entity teleportToEntity(Entity entity, Entity target) {
        if (entity == null || target == null) {
            return entity;
        }

        Position targetPos = Position.fromEntity(target);
        return teleportEntity(entity, targetPos);
    }

    /**
     * Check if teleportation to a position is safe.
     * Checks for solid ground and non-suffocating blocks.
     *
     * @param level The level to check in
     * @param position The position to check
     * @return true if position is safe for teleportation
     */
    public static boolean isSafePosition(Level level, Position position) {
        if (level == null || position == null) {
            return false;
        }

        // Check if there's solid ground below
        var groundPos = position.toBlockPos().below();
        var groundState = level.getBlockState(groundPos);

        if (!groundState.isSolid()) {
            return false; // No solid ground
        }

        // Check if the position itself is not solid (entity can fit)
        var feetPos = position.toBlockPos();
        var headPos = feetPos.above();

        var feetState = level.getBlockState(feetPos);
        var headState = level.getBlockState(headPos);

        return !feetState.isSolid() && !headState.isSolid();
    }

    /**
     * Find a safe position near the target position.
     * Searches in a small radius for a valid teleport location.
     *
     * @param level The level to search in
     * @param position The target position
     * @param radius Search radius
     * @return Safe position or original if none found
     */
    public static Position findSafePosition(
        Level level,
        Position position,
        int radius
    ) {
        if (level == null || position == null) {
            return position;
        }

        // First check if original position is safe
        if (isSafePosition(level, position)) {
            return position;
        }

        // Search in expanding circles
        for (int r = 1; r <= radius; r++) {
            for (int x = -r; x <= r; x++) {
                for (int z = -r; z <= r; z++) {
                    // Only check edge of current radius
                    if (Math.abs(x) != r && Math.abs(z) != r) continue;

                    Position testPos = new Position(
                        position.getX() + x,
                        position.getY(),
                        position.getZ() + z,
                        position.getYaw(),
                        position.getPitch(),
                        position.getDimension()
                    );

                    if (isSafePosition(level, testPos)) {
                        return testPos;
                    }
                }
            }
        }

        // No safe position found, return original
        TiedUpMod.LOGGER.warn(
            "[TeleportHelper] No safe position found near {}",
            position.toShortString()
        );
        return position;
    }

    // CUSTOM TELEPORTER FOR FORGE

    /**
     * Custom teleporter that preserves exact position and rotation.
     */
    private static class PositionTeleporter implements ITeleporter {

        private final Position position;

        public PositionTeleporter(Position position) {
            this.position = position;
        }

        @Override
        public Entity placeEntity(
            Entity entity,
            ServerLevel currentWorld,
            ServerLevel destWorld,
            float yaw,
            Function<Boolean, Entity> repositionEntity
        ) {
            // Reposition the entity first (handles mounting, leashing, etc.)
            Entity repositioned = repositionEntity.apply(false);

            if (repositioned != null) {
                // Set exact position and rotation
                repositioned.teleportTo(
                    position.getX(),
                    position.getY(),
                    position.getZ()
                );
                repositioned.setYRot(position.getYaw());
                repositioned.setXRot(position.getPitch());
                repositioned.setYHeadRot(position.getYaw());
            }

            return repositioned;
        }

        @Nullable
        @Override
        public PortalInfo getPortalInfo(
            Entity entity,
            ServerLevel destWorld,
            Function<ServerLevel, PortalInfo> defaultPortalInfo
        ) {
            return new PortalInfo(
                new Vec3(position.getX(), position.getY(), position.getZ()),
                Vec3.ZERO, // No velocity
                position.getYaw(),
                position.getPitch()
            );
        }

        @Override
        public boolean isVanilla() {
            return false; // Custom teleporter
        }

        @Override
        public boolean playTeleportSound(
            ServerPlayer player,
            ServerLevel sourceWorld,
            ServerLevel destWorld
        ) {
            return false; // No teleport sound
        }
    }
}
