/*
 *  Copyright 2025-2026 R3944Realms
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

/*
 * 座椅管理服务类 - 统一管理座椅绑定、释放和动画逻辑
 */
package top.r3944realms.eroticdungeongame.core.service;

import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.NotNull;
import top.r3944realms.eroticdungeongame.EroticDungeon;
import top.r3944realms.eroticdungeongame.api.EroticDungeonGameApi;
import top.r3944realms.eroticdungeongame.api.event.RideDeviceEvent;
import top.r3944realms.eroticdungeongame.api.event.UnRideDeviceEvent;
import top.r3944realms.eroticdungeongame.content.animation.EDGAnimation;
import top.r3944realms.eroticdungeongame.content.block.AbstractSeatBlock;
import top.r3944realms.eroticdungeongame.content.block.blockentity.BaseSeatBlockEntity;
import top.r3944realms.eroticdungeongame.content.block.multiply.AbstractTwoPartSeatBlock;
import top.r3944realms.eroticdungeongame.content.block.multiply.IHasPrimaryPart;
import top.r3944realms.eroticdungeongame.content.block.part.SeatPart;
import top.r3944realms.eroticdungeongame.content.block.type.PilloryBlock;
import top.r3944realms.eroticdungeongame.content.entity.SeatEntity;
import top.r3944realms.eroticdungeongame.content.util.FurnitureHelper;
import top.r3944realms.eroticdungeongame.core.device.ISeatType;
import top.r3944realms.eroticdungeongame.core.register.EDGEntities;
import top.r3944realms.eroticdungeongame.util.IEDGEntity;

import java.util.Objects;
import java.util.Optional;

public class SeatService {

    private SeatService() {}

    public static boolean bindPlayerToSeat(@NotNull Level level, @NotNull BlockPos blockPos,
                                           @NotNull Player player, @NotNull ISeatType ISeatType) {
        return bindPlayerToSeatInternal(level, blockPos, player, ISeatType);
    }
    public static boolean bindPlayerToSeat(@NotNull Player player, @NotNull BlockPos blockPos) throws NullPointerException {
        return bindPlayerToSeatInternal(player.level(), blockPos, player, Objects.requireNonNull(FurnitureHelper.getSeatType(player.level().getBlockState(blockPos).getBlock()), "Block is not EDG Seat Block"));
    }

    private static boolean bindPlayerToSeatInternal(@NotNull Level level, @NotNull BlockPos blockPos, @NotNull Player player, @NotNull ISeatType ISeatType) {
        if (level.isClientSide || player.isPassenger()) {
            return false;
        }

        // 1. 获取方块状态和方块实体
        BlockState blockState = level.getBlockState(blockPos);
        BaseSeatBlockEntity blockEntity = FurnitureHelper.getSeatBlockEntity(level, blockPos);

        if (blockEntity == null || !(blockState.getBlock() instanceof AbstractSeatBlock)) {
            return false;
        } else if (blockState.getBlock() instanceof IHasPrimaryPart<?> primaryPart) {
            blockPos = primaryPart.getPrimaryPartPos(blockState, blockPos);
            blockState = level.getBlockState(blockPos);
        }
        if (blockState.getValue(AbstractSeatBlock.OCCUPIED) && blockEntity.getBoundPlayerUUID() != null) {
            Player playerByUUID = level.getPlayerByUUID(blockEntity.getBoundPlayerUUID());
            if (playerByUUID != null) {
                return false;
            }
        }
        boolean isCancelled = EroticDungeon.EVENT_BUS.post(new RideDeviceEvent.Pre(player, blockPos, blockState, blockEntity));
        if (isCancelled) {
            return false;
        }
        // 2. 计算座椅位置和朝向
        Direction direction = calculateFinalDirection(blockState, ISeatType);
        SeatPositionResult positionResult = calculateSeatPosition(blockPos, direction, ISeatType);

        // 3. 创建座椅实体
        Optional<SeatEntity> seatEntityOpt = createSeatEntity(level, positionResult);
        if (seatEntityOpt.isEmpty()) {
            return false;
        }
        if (blockState.getBlock() instanceof PilloryBlock ) {
            if (blockState.getValue(AbstractTwoPartSeatBlock.PART) == SeatPart.HEAD) {
                FurnitureHelper.setBlockState(level, blockPos, PilloryBlock.CLOSED,true);
            } else FurnitureHelper.setBlockState(level, blockPos.above(), PilloryBlock.CLOSED,true);

        }

        SeatEntity seatEntity = seatEntityOpt.get();
        EroticDungeon.EVENT_BUS.post(new RideDeviceEvent.Post(player, blockPos, blockState, blockEntity, seatEntity));
        // 4. 设置关联并更新状态
        setupSeatRelationships(seatEntity, blockEntity, player, blockPos, ISeatType, blockEntity.getPlayerAnimationNumber());

        // 5. 绑定玩家
        if (!player.startRiding(seatEntity, true)) {
            seatEntity.discard();
            return false;
        }
        return true;
    }

    /**
     * 从方块位置释放玩家
     */
    public static boolean releasePlayerFromBlock(@NotNull Level level, @NotNull BlockPos blockPos) {
        BaseSeatBlockEntity blockEntity = FurnitureHelper.getSeatBlockEntity(level, blockPos);
        if (blockEntity != null) {
            return releasePlayer(level, blockPos);
        }
        return false;
    }

    public static @NotNull Optional<SeatEntity> getPlayerSeat(@NotNull Player player) {
        return player.getCapability(EroticDungeonGameApi.PLAYER_DUNGEON_DATA_CAP).resolve().map(cap -> {
            BlockPos deviceMainBlockPos = cap.getDeviceMainBlockPos();
            if (player.isPassenger() || deviceMainBlockPos != null) {
                Entity vehicle = player.getVehicle();
                if (vehicle instanceof SeatEntity seat) {
                    boolean flag = Objects.equals(seat.getLinkedBlockPos(), deviceMainBlockPos);
                    if (flag) {
                        return seat;
                    }
                }
            }
            return null;
        });
    }

    public static @NotNull Optional<SeatEntity> getOrCreatePlayerSeat(@NotNull Player player) {
        return player.getCapability(EroticDungeonGameApi.PLAYER_DUNGEON_DATA_CAP).resolve().map(cap -> {
            BlockPos deviceMainBlockPos = cap.getDeviceMainBlockPos();
            if (player.isPassenger() && deviceMainBlockPos != null) {
                Entity vehicle = player.getVehicle();
                if (vehicle instanceof SeatEntity seat) {
                    boolean flag = Objects.equals(seat.getLinkedBlockPos(), deviceMainBlockPos);
                    if (flag) {
                        return seat;
                    }
                }
            } else if (deviceMainBlockPos != null && player.level().getBlockEntity(cap.getDeviceMainBlockPos()) instanceof BaseSeatBlockEntity seat) {
                ((IEDGEntity)player).releaseEntityFromEDGDevice(seat.getLockCode());
                try {
                    if (bindPlayerToSeat(player, deviceMainBlockPos)) {
                        return player.getVehicle();
                    }
                } catch (NullPointerException e) {
                    EroticDungeon.getLogger().warn("Can't bind player to Seat, error: {}",e.getMessage(), e);
                    cap.clearDungeonData(player);
                }
            }
            return null;
        }).map(SeatEntity.class::cast);
    }

    // ================= 私有方法 =================

    private static Direction calculateFinalDirection(@NotNull BlockState blockState,
                                                     @NotNull ISeatType ISeatType) {
        Direction original = blockState.getValue(AbstractSeatBlock.FACING);
        return ISeatType.isOppositeDirection() ? original.getOpposite() : original;
    }

    private static Optional<SeatEntity> createSeatEntity(@NotNull Level level,
                                                         @NotNull SeatPositionResult positionResult) {
        SeatEntity seatEntity = EDGEntities.SEAT.get().create(level);
        if (seatEntity == null) {
            return Optional.empty();
        }

        seatEntity.setPos(positionResult.x, positionResult.y, positionResult.z);
        seatEntity.setYRot(positionResult.yaw);
        seatEntity.setYBodyRot(positionResult.yaw);

        level.addFreshEntity(seatEntity);
        return Optional.of(seatEntity);
    }

    private static void setupSeatRelationships(@NotNull SeatEntity seatEntity,
                                               @NotNull BaseSeatBlockEntity blockEntity,
                                               @NotNull Player player,
                                               @NotNull BlockPos blockPos, @NotNull ISeatType ISeatType, int varNumber) {
        // 设置座椅关联
        seatEntity.setLinkedBlockPos(blockPos);

        // 更新方块实体数据
        blockEntity.setBoundPlayerUUID(player.getUUID());

        // 更新方块状态
        FurnitureHelper.setSeatOccupied(player.level(), blockPos, true);

        // 同步玩家朝向
        player.setYRot(seatEntity.getYRot());
        player.setYBodyRot(seatEntity.getYRot());

        // 设置玩家cap
        player.getCapability(EroticDungeonGameApi.PLAYER_DUNGEON_DATA_CAP).resolve()
                .ifPresent(cap -> cap.setDungeonData(ISeatType, varNumber, seatEntity));
    }

    private static void updateSeatBlockState(@NotNull Level level, @NotNull BlockPos blockPos,
                                             boolean occupied) {
        BlockState blockState = level.getBlockState(blockPos);
        if (blockState.hasProperty(AbstractSeatBlock.OCCUPIED)) {
            level.setBlock(blockPos,
                    blockState.setValue(AbstractSeatBlock.OCCUPIED, occupied),
                    Block.UPDATE_ALL
            );
        }
    }

    public static void playBindingAnimation(@NotNull Player player, @NotNull ISeatType ISeatType) {
        EDGAnimation animation = ISeatType.getAnimation();
        if (animation != null) {
            animation.play(player);
        }
    }
    public static void playBindingAnimation(@NotNull Player player, @NotNull ISeatType ISeatType, int varNumber) {
        EDGAnimation animation = ISeatType.getAnimation();
        if (animation != null) {
            animation.play(player, varNumber);
        }
    }

    /**
     * 释放玩家
     */
    public static boolean releasePlayer(@NotNull Level level, BlockPos blockPos) {
        BaseSeatBlockEntity seatBlockEntity = FurnitureHelper.getSeatBlockEntity(level, blockPos);
        return releasePlayerInternal(seatBlockEntity, level, blockPos);
    }

    private static boolean releasePlayerInternal(BaseSeatBlockEntity be, @NotNull Level level, BlockPos blockPos) {
        BlockState blockState = level.getBlockState(blockPos);
        // 1. 更新方块状态
        FurnitureHelper.setSeatOccupied(level, blockPos, false);

        // 2. 处理实体（只在服务端）
        if (level instanceof ServerLevel) {
            Player playerByUUID;
            boolean isCancelled;
            if (be.getBoundPlayerUUID() != null) {
                playerByUUID = level.getPlayerByUUID(be.getBoundPlayerUUID());
                if (playerByUUID != null) {
                    SeatEntity seat = null;
                    if (playerByUUID.isPassenger() && playerByUUID.getVehicle() instanceof SeatEntity seatEntity) {
                       seat = seatEntity;
                    }
                    isCancelled = EroticDungeon.EVENT_BUS.post(new UnRideDeviceEvent.Pre(playerByUUID, blockPos, blockState, be, seat));
                    if (!isCancelled) {
                        if(seat != null) ((IEDGEntity)(playerByUUID)).releaseEntityFromEDGDevice(be.getLockCode());
                        playerByUUID.getCapability(EroticDungeonGameApi.PLAYER_DUNGEON_DATA_CAP)
                                .ifPresent(i -> i.clearDungeonData(playerByUUID));
                    } else {
                        bindPlayerToSeat(playerByUUID, blockPos);
                        return false;
                    }
                }
            } else {
                playerByUUID = null;
            }

            // 停止动画
            if (playerByUUID != null) {
                EDGAnimation.stop(playerByUUID);
                EroticDungeon.EVENT_BUS.post(new UnRideDeviceEvent.Post(playerByUUID, blockPos, blockState, be));
            }
        }

        // 3. 清理数据
        be.setBoundPlayerUUID(null);
        be.setChanged();
        return true;
    }

    // ================= 辅助类 =================

    private record SeatPositionResult(double x, double y, double z, float yaw) {}

    private static @NotNull SeatPositionResult calculateSeatPosition(@NotNull BlockPos blockPos,
                                                                     @NotNull Direction direction,
                                                                     @NotNull ISeatType ISeatType) {
        double[] posArray = calculatePositionArray(blockPos, direction, ISeatType);
        float yaw = calculateYaw(direction, ISeatType);

        return new SeatPositionResult(posArray[0], posArray[1], posArray[2], yaw);
    }

    private static double @NotNull [] calculatePositionArray(@NotNull BlockPos blockPos,
                                                             @NotNull Direction direction,
                                                             @NotNull ISeatType ISeatType) {
        double baseX = blockPos.getX();
        double baseY = blockPos.getY() + 0.5;
        double baseZ = blockPos.getZ();

        double offsetX = ISeatType.getOffsetX();
        double offsetY = ISeatType.getOffsetY();
        double offsetZ = ISeatType.getOffsetZ();

        // 如果isOppositeDirection为true，则取反x和z偏移量
        if (ISeatType.isOppositeDirection()) {
            offsetX = -offsetX;
            offsetZ = -offsetZ;
        }

        // 计算方向向量
        double frontX = direction.getStepX();
        double frontZ = direction.getStepZ();
        double rightX = frontZ;
        double rightZ = -frontX;

        // 计算左边前边角位置
        double leftFrontCornerX;
        double leftFrontCornerZ;

        // 左边前边角 = 没有向右偏移 + 向前1个单位
        // 更准确地说：左边 = 右方向的相反方向，前边 = 前方向

        if (frontX != 0) { // EAST或WEST方向
            // 对于东西方向，左边是右方向的z分量的相反方向
            leftFrontCornerX = frontX > 0 ? 0 : 1; // 东：x=0，西：x=1

            // 右边方向z>0表示南，我们要左边（北）
            // 右边方向z<0表示北，我们要左边（南）
            if (rightZ > 0) { // 右边是南
                leftFrontCornerZ = 1; // 左边是北（z=1）
            } else { // 右边是北
                leftFrontCornerZ = 0; // 左边是南（z=0）
            }
        } else { // NORTH或SOUTH方向
            // 对于南北方向，左边是右方向的x分量的相反方向
            leftFrontCornerZ = frontZ > 0 ? 0 : 1; // 南：z=0，北：z=1

            //右边方向x>0表示东，我们要左边（西）
            // 右边方向x<0表示西，我们要左边（东）
            if (rightX > 0) { // 右边是东
                leftFrontCornerX = 1; // 左边是西（x=1）
            } else { // 右边是西
                leftFrontCornerX = 0; // 左边是东（x=0）
            }
        }

        // 计算最终位置
        double finalX = baseX + leftFrontCornerX + rightX * offsetX + frontX * offsetZ;
        double finalZ = baseZ + leftFrontCornerZ + rightZ * offsetX + frontZ * offsetZ;

        return new double[]{finalX, baseY + offsetY, finalZ};
    }

    private static float calculateYaw(@NotNull Direction direction, ISeatType ISeatType) {
        float baseYaw = switch (direction) {
            case NORTH -> 0.0f;
            case WEST -> 270.0f;
            case EAST -> 90.0f;
            default -> 180.0f;
        };

        float offset = ISeatType.isOppositeDirection() ?
                -ISeatType.getYawOffset() : ISeatType.getYawOffset();

        return baseYaw + offset;
    }
}
