package com.r3944realms.modernlifepatch.content.blocks.type.mirror;

import com.r3944realms.modernlifepatch.content.blocks.blockstate.ModBlockStateProperties;
import com.r3944realms.modernlifepatch.content.blocks.entities.type.mirror.MirrorBlockEntity;
import com.r3944realms.modernlifepatch.content.blocks.type.mirror.shape.IMirrorShape;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.LevelReader;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;


public class MirrorBlock extends HorizontalDirectionalBlock implements EntityBlock, SimpleWaterloggedBlock {
    public static final EnumProperty<MirrorPart> PART = ModBlockStateProperties.MIRROR_PART;
    public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
    public final MirrorPart.MirrorType mirrorType;

    public MirrorBlock(Properties pProperties, MirrorPart.@NotNull MirrorType mirrorType) {
        super(pProperties);
        MirrorPart mirrorPart = null;
        this.mirrorType = mirrorType;
        switch (mirrorType) {
            case COMMON_MIRROR -> mirrorPart = MirrorPart.SINGLE;
            case TELL_MIRROR -> mirrorPart = MirrorPart.FOOT;
            case LARGE_MIRROR -> mirrorPart = MirrorPart.FOOT_LEFT;
            case MASSIVE_MIRROR -> mirrorPart = MirrorPart.F11;
        }
        this.registerDefaultState(this.stateDefinition.any().setValue(PART, mirrorPart).setValue(WATERLOGGED, false));
    }
    @Override
    public @NotNull FluidState getFluidState(@NotNull BlockState pState) {
        return pState.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : Fluids.EMPTY.defaultFluidState();
    }
    // 控制是否能够在该放置放置方块， 如果不能满足放置要求则返回null（借鉴于原版的梯子方块）
    @Nullable
    @Override
    public BlockState getStateForPlacement(@NotNull BlockPlaceContext pContext) {
        final BlockPos clickedPos = pContext.getClickedPos();
        final Level level = pContext.getLevel();
        final Direction horizontalDirection = pContext.getHorizontalDirection();
        FluidState fluidState$Root = pContext.getLevel().getFluidState(clickedPos);
        return switch (mirrorType) {
            case COMMON_MIRROR -> {
                // 检查后部分是否有合法的支撑方块
                BlockPos behind = clickedPos.relative(horizontalDirection);

                boolean flag = !level.getBlockState(behind).isAir() && level.getBlockState(behind).isSolidRender(level, behind);
                yield flag ? this.defaultBlockState().setValue(FACING, horizontalDirection).setValue(WATERLOGGED, fluidState$Root.getType() == Fluids.WATER) : null;
            }
            case TELL_MIRROR -> {
                // 检查上部分是不是有阻挡
                BlockPos above = clickedPos.above();
                boolean flag1 = level.getBlockState(above).canBeReplaced(pContext);
                // 检查上部分是否有合法的支撑方块
                BlockPos relative = above.relative(horizontalDirection);
                boolean flag2 = !level.getBlockState(relative).isAir() && level.getBlockState(relative).isSolidRender(level, relative);
                yield flag1 && flag2 ? this.defaultBlockState().setValue(FACING, horizontalDirection).setValue(WATERLOGGED, fluidState$Root.getType() == Fluids.WATER) : null;
            }
            case LARGE_MIRROR -> {
                // 检查空间是否足够2x2
                BlockPos headLeft = clickedPos.above(),
                        footRight = clickedPos.relative(pContext.getHorizontalDirection().getCounterClockWise().getOpposite()),
                        headRight = footRight.above();
                boolean flag1 = level.getBlockState(headLeft).canBeReplaced(pContext) &&
                        level.getBlockState(footRight).canBeReplaced(pContext) &&
                        level.getBlockState(headRight).canBeReplaced(pContext);
                // 检查其它部分是否有合法的支撑方块
                BlockPos headLeftSupport = headLeft.relative(horizontalDirection),
                        headRightSupport = headRight.relative(horizontalDirection),
                        footRightSupport = footRight.relative(horizontalDirection);
                boolean flag2 = (!level.getBlockState(headLeftSupport).isAir() && level.getBlockState(headLeftSupport).isSolidRender(level, headLeftSupport)) &&
                        (!level.getBlockState(footRightSupport).isAir() && level.getBlockState(footRightSupport).isSolidRender(level, footRightSupport)) &&
                        (!level.getBlockState(headRightSupport).isAir() && level.getBlockState(headRightSupport).isSolidRender(level, headRightSupport));
                yield flag1 && flag2 ? this.defaultBlockState().setValue(FACING, horizontalDirection).setValue(WATERLOGGED, fluidState$Root.getType() == Fluids.WATER) : null;
            }
            case MASSIVE_MIRROR -> {
                // 检查空间是否足够2x3
                BlockPos footLeft = clickedPos.relative(pContext.getHorizontalDirection().getCounterClockWise()),
                        headLeft = footLeft.above(),
                        footRight = clickedPos.relative(pContext.getHorizontalDirection().getCounterClockWise().getOpposite()),
                        headMiddle = clickedPos.above(),
                        headRight = footRight.above();
                boolean flag1 = level.getBlockState(headLeft).canBeReplaced(pContext) &&
                        level.getBlockState(footRight).canBeReplaced(pContext) &&
                        level.getBlockState(footLeft).canBeReplaced(pContext) &&
                        level.getBlockState(headMiddle).canBeReplaced(pContext) &&
                        level.getBlockState(headRight).canBeReplaced(pContext);
                // 检查其它部分是否有合法的支撑方块
                BlockPos headLeftSupport = headLeft.relative(horizontalDirection),
                        headMiddleSupport = headMiddle.relative(horizontalDirection),
                        headRightSupport = headRight.relative(horizontalDirection),
                        footLeftSupport = footLeft.relative(horizontalDirection),
                        footRightSupport = footRight.relative(horizontalDirection);
                boolean flag2 = (!level.getBlockState(headLeftSupport).isAir() && level.getBlockState(headLeftSupport).isSolidRender(level, headLeftSupport)) &&
                        (!level.getBlockState(footRightSupport).isAir() && level.getBlockState(footRightSupport).isSolidRender(level, footRightSupport)) &&
                        (!level.getBlockState(headRightSupport).isAir() && level.getBlockState(headRightSupport).isSolidRender(level, headRightSupport)) &&
                        (!level.getBlockState(headMiddleSupport).isAir() && level.getBlockState(headMiddleSupport).isSolidRender(level, headMiddleSupport)) &&
                        (!level.getBlockState(footLeftSupport).isAir() && level.getBlockState(footLeftSupport).isSolidRender(level, footLeftSupport));
                yield flag1 && flag2 ? this.defaultBlockState().setValue(FACING, horizontalDirection).setValue(WATERLOGGED, fluidState$Root.getType() == Fluids.WATER) : null;
            }
        };
    }

    // 控制当方块结构不完整时，破坏另一结构的方块（借鉴于于原版的床破坏逻辑）
    @Override
    public @NotNull BlockState updateShape(@NotNull BlockState pState, @NotNull Direction pDirection, @NotNull BlockState pNeighborState, @NotNull LevelAccessor pLevel, @NotNull BlockPos pCurrentPos, @NotNull BlockPos pNeighborPos) {
        if (pState.getValue(WATERLOGGED)) {
            pLevel.scheduleTick(pCurrentPos, Fluids.WATER, Fluids.WATER.getTickDelay(pLevel));
        }
        final boolean canSelfNotSurvive = !pState.canSurvive(pLevel, pCurrentPos);
        final Direction facing = pState.getValue(FACING);
        final MirrorPart part = pState.getValue(PART);
        return switch (mirrorType) {
            case COMMON_MIRROR -> (pDirection == facing && canSelfNotSurvive) ?
                    Blocks.AIR.defaultBlockState() : pState;
            case TELL_MIRROR -> {
                boolean isFoot = part == MirrorPart.FOOT;
                BlockPos otherPartPos = isFoot ? pCurrentPos.above() : pCurrentPos.below();
                BlockState otherPartState = pLevel.getBlockState(otherPartPos);
                // 当自身无法存活时，检查另一部分是否存在，并一起破坏
                boolean flag = otherPartState.is(this) && otherPartState.getValue(PART) == (isFoot ? MirrorPart.HEAD : MirrorPart.FOOT);
                if (canSelfNotSurvive) {
                    // 破坏自身
                    pLevel.destroyBlock(pCurrentPos, true);
                    // 若另一部分存在，则破坏另一部分
                    pLevel.destroyBlock(otherPartPos, false);
                    yield Blocks.AIR.defaultBlockState();
                }
                if(!flag) {
                    pLevel.destroyBlock(otherPartPos, true);
                    pLevel.destroyBlock(pCurrentPos, false);
                }
                yield  flag ? pState : Blocks.AIR.defaultBlockState();
            }
            case LARGE_MIRROR -> {
                boolean isMainPart = part == MirrorPart.FOOT_LEFT;
                BlockPos[] dependentPositions = (isMainPart ? getAllLargeMirrorPosByMainPos(pCurrentPos, facing) : findLargeMirrorMainPartPositions(pCurrentPos, pState));
                // 统一处理所有关联部件
                boolean flag = canSelfNotSurvive || isAnyCriticalPartMissing(pLevel, dependentPositions);
                if (canSelfNotSurvive) {
                    pLevel.destroyBlock(pCurrentPos, true);
                    destroyEntireStructure(pLevel, pCurrentPos, dependentPositions);
                } else if(isAnyCriticalPartMissing(pLevel, dependentPositions)) {
                    pLevel.destroyBlock(pCurrentPos, false);
                    destroyEntireStructure(pLevel, pCurrentPos, dependentPositions);
                }
                yield flag ?
                    Blocks.AIR.defaultBlockState() : pState;
            }
            case MASSIVE_MIRROR -> {
                boolean isMainPart = part == MirrorPart.F11;
                BlockPos[] dependentPositions = isMainPart ? getAllMassiveMirrorPartsPosByMainPos(pCurrentPos, facing) : findMassiveMirrorMainPartPositions(pCurrentPos, pState);
                // 统一处理所有关联部件
                boolean flag = canSelfNotSurvive || isAnyCriticalPartMissing(pLevel, dependentPositions);
                if (canSelfNotSurvive) {
                    pLevel.destroyBlock(pCurrentPos, true);
                    destroyEntireStructure(pLevel, pCurrentPos, dependentPositions);
                } else if(isAnyCriticalPartMissing(pLevel, dependentPositions)) {
                    pLevel.destroyBlock(pCurrentPos, false);
                    destroyEntireStructure(pLevel, pCurrentPos, dependentPositions);
                }
                yield flag ?
                         Blocks.AIR.defaultBlockState() : pState;
            }
        };
    }

    // 控制实体放置一部分方块后，自动放置另一部分（借鉴于于原版的床放置逻辑）
    @Override
    public void setPlacedBy(@NotNull Level pLevel, @NotNull BlockPos pPos, @NotNull BlockState pState, @Nullable LivingEntity pPlacer, @NotNull ItemStack pStack) {
        super.setPlacedBy(pLevel, pPos, pState, pPlacer, pStack);
        if(!pLevel.isClientSide) {
            final Direction facing = pState.getValue(FACING);
            switch (mirrorType) {
                case COMMON_MIRROR -> {
                    //NOOP
                }
                case TELL_MIRROR -> {
                    BlockPos blockpos = pPos.above();
                    pLevel.setBlock(blockpos, pState.setValue(PART, MirrorPart.HEAD).setValue(WATERLOGGED, pLevel.getFluidState(blockpos).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.blockUpdated(pPos, Blocks.AIR);
                    pState.updateNeighbourShapes(pLevel, pPos,  Block.UPDATE_ALL);
                }
                case LARGE_MIRROR -> {
                    BlockPos headLeft = pPos.above(), footRight = pPos.relative(facing.getCounterClockWise().getOpposite()), headRight = footRight.above();
                    pLevel.setBlock(headLeft, pState.setValue(PART, MirrorPart.HEAD_LEFT).setValue(WATERLOGGED, pLevel.getFluidState(headLeft).getType() == Fluids.WATER),  Block.UPDATE_ALL);
                    pLevel.setBlock(footRight, pState.setValue(PART, MirrorPart.FOOT_RIGHT).setValue(WATERLOGGED, pLevel.getFluidState(footRight).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.setBlock(headRight, pState.setValue(PART, MirrorPart.HEAD_RIGHT).setValue(WATERLOGGED, pLevel.getFluidState(headRight).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.blockUpdated(pPos, Blocks.AIR);
                    pState.updateNeighbourShapes(pLevel, pPos,  Block.UPDATE_ALL);
                }
                case MASSIVE_MIRROR -> {
                    BlockPos h01 = pPos.above(),
                            f10 = pPos.relative(facing.getCounterClockWise()),
                            f12 = pPos.relative(facing.getCounterClockWise().getOpposite()),
                            h00 = f10.above(),
                            h02 = f12.above();
                    pLevel.setBlock(h00, pState.setValue(PART, MirrorPart.H00).setValue(WATERLOGGED, pLevel.getFluidState(h00).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.setBlock(f10, pState.setValue(PART, MirrorPart.F10).setValue(WATERLOGGED, pLevel.getFluidState(f10).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.setBlock(h01, pState.setValue(PART, MirrorPart.H01).setValue(WATERLOGGED, pLevel.getFluidState(h01).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.setBlock(f12, pState.setValue(PART, MirrorPart.F12).setValue(WATERLOGGED, pLevel.getFluidState(f12).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.setBlock(h02, pState.setValue(PART, MirrorPart.H02).setValue(WATERLOGGED, pLevel.getFluidState(h02).getType() == Fluids.WATER), Block.UPDATE_ALL);
                    pLevel.blockUpdated(pPos, Blocks.AIR);
                    pState.updateNeighbourShapes(pLevel, pPos, Block.UPDATE_ALL);
                }
            }
        }
    }
    private BlockPos @NotNull [] findMassiveMirrorMainPartPositions(BlockPos currentPos, @NotNull BlockState state) {
        Direction facing = state.getValue(FACING);
        MirrorPart part = state.getValue(PART);

        // 根据部件类型反向计算主部件位置
        return switch (part) {
            case H00 -> getAllMassiveMirrorPartsPosByMainPos(currentPos.relative(facing.getClockWise()).below(), facing);
            case H01 -> getAllMassiveMirrorPartsPosByMainPos(currentPos.below(), facing);
            case H02 -> getAllMassiveMirrorPartsPosByMainPos(currentPos.relative(facing.getClockWise().getOpposite()).below(),facing);
            case F10 -> getAllMassiveMirrorPartsPosByMainPos(currentPos.relative(facing.getClockWise()), facing);
            case F12 -> getAllMassiveMirrorPartsPosByMainPos(currentPos.relative(facing.getClockWise().getOpposite()), facing);
            default -> throw new IllegalStateException("Unexpected value: " + part);
        };
    }
    // 辅助方法：获取MASSIVE_MIRROR所有部件坐标
    private BlockPos @NotNull [] getAllMassiveMirrorPartsPosByMainPos(@NotNull BlockPos origin, @NotNull Direction facing) {
        BlockPos f10 = origin.relative(facing.getCounterClockWise());
        BlockPos f12 = origin.relative(facing.getCounterClockWise().getOpposite());
        return new BlockPos[]{
                f10,                  // F10
                f10.above(),          // H00
                origin,               // F11
                origin.above(),       // H01
                f12,                  // F12
                f12.above()           // H02
        };
    }

    // 辅助方法：破坏整个结构
    private void destroyEntireStructure(LevelAccessor level, BlockPos currentPos, BlockPos @NotNull [] parts) {
        for (BlockPos pos : parts) {
            if (pos.equals(currentPos)) continue; // 跳过当前已处理的方块
            if (level.getBlockState(pos).getBlock() == this) {
                level.destroyBlock(pos, false);
            }
        }
    }

    // 辅助方法：检查关键部件是否缺失
    private boolean isAnyCriticalPartMissing(LevelAccessor level, BlockPos @NotNull [] parts) {
        for (BlockPos pos : parts) {
            if (!level.getBlockState(pos).is(this)) {
                return true;
            }
        }
        return false;
    }

    /**
     *
     * @return [0]->FL [1]->HL [2]->FR [3]->HR
     */
    private static BlockPos @NotNull [] getAllLargeMirrorPosByMainPos(@NotNull BlockPos pCurrentPos, @NotNull Direction facing) {
        BlockPos footRight = pCurrentPos.relative(facing.getCounterClockWise().getOpposite());
        return new BlockPos[]{pCurrentPos, pCurrentPos.above(), footRight, footRight.above()};
    }

    // 用于LARGE_MIRROR反向查找主部件并以此组装有序的数组
    private BlockPos @NotNull [] findLargeMirrorMainPartPositions(BlockPos currentPos, @NotNull BlockState state) {
        Direction facing = state.getValue(FACING);
        MirrorPart part = state.getValue(PART);

        // 根据部件类型反向计算主部件位置
        return switch (part) {
            case HEAD_LEFT -> getAllLargeMirrorPosByMainPos(currentPos.below(), facing);
            case FOOT_RIGHT -> getAllLargeMirrorPosByMainPos(currentPos.relative(facing.getClockWise().getOpposite()), facing);
            case HEAD_RIGHT -> getAllLargeMirrorPosByMainPos(currentPos.below().relative(facing.getClockWise().getOpposite()),facing);
            default -> throw new IllegalStateException("Unexpected value: " + part);
        };
    }

    @Override
    public @NotNull VoxelShape getShape(@NotNull BlockState pState, @NotNull BlockGetter pLevel, @NotNull BlockPos pPos, @NotNull CollisionContext pContext) {
        MirrorPart part = pState.getValue(PART);
        Direction direction = pState.getValue(FACING);
        return IMirrorShape.getShapeInstanceByPart(part).getShapeByPart(direction, part);
    }

    @Override
    public @NotNull InteractionResult use(@NotNull BlockState pState, @NotNull Level pLevel, @NotNull BlockPos pPos, @NotNull Player pPlayer, @NotNull InteractionHand pHand, @NotNull BlockHitResult pHit) {
        if (pLevel.isClientSide) {
            return InteractionResult.CONSUME;
        } else {
            return InteractionResult.PASS;
        }
    }

    @Nullable
    @Override
    public BlockEntity newBlockEntity(@NotNull BlockPos pPos, @NotNull BlockState pState) {
        return new MirrorBlockEntity(pPos, pState);
    }

    private boolean canAttachTo(@NotNull BlockGetter pBlockReader, BlockPos pPos, Direction direction) {
        BlockState blockState = pBlockReader.getBlockState(pPos);
        return blockState.isFaceSturdy(pBlockReader, pPos, direction);
    }

    @Override
    public boolean canSurvive(@NotNull BlockState pState, @NotNull LevelReader pLevel, @NotNull BlockPos pPos) {
        Direction direction = pState.getValue(FACING);
        return this.canAttachTo(pLevel, pPos.relative(direction), direction);
    }


    protected void createBlockStateDefinition(StateDefinition.@NotNull Builder<Block, BlockState> pBuilder) {
        pBuilder.add(FACING, PART, WATERLOGGED);
    }
}
