package com.extendedae_plus.content.wireless;

import appeng.api.networking.GridFlags;
import appeng.api.networking.GridHelper;
import appeng.api.networking.IGridNode;
import appeng.api.networking.IGridNodeListener;
import appeng.api.networking.IInWorldGridNodeHost;
import appeng.api.networking.IManagedGridNode;
import appeng.api.util.AECableType;
import appeng.blockentity.AEBaseBlockEntity;
import com.extendedae_plus.ae.wireless.IWirelessEndpoint;
import com.extendedae_plus.ae.wireless.LabelLink;
import com.extendedae_plus.ae.wireless.LabelNetworkRegistry;
import com.extendedae_plus.config.ModConfig;
import com.extendedae_plus.init.ModBlockEntities;
import com.extendedae_plus.init.ModItems;
import com.extendedae_plus.menu.LabeledWirelessTransceiverMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import org.jetbrains.annotations.Nullable;

import java.util.EnumSet;
import java.util.Objects;
import java.util.UUID;

public class LabeledWirelessTransceiverBlockEntity extends AEBaseBlockEntity implements IWirelessEndpoint, IInWorldGridNodeHost, MenuProvider {

    private IManagedGridNode managedNode;

    private long frequency = 0L;
    @Nullable
    private String labelForDisplay;
    private boolean beingRemoved = false;

    @Nullable
    private UUID placerId;
    @Nullable
    private String placerName;

    private final LabelLink labelLink = new LabelLink(this);

    public LabeledWirelessTransceiverBlockEntity(BlockPos pos, BlockState state) {
        super(ModBlockEntities.LABELED_WIRELESS_TRANSCEIVER_BE.get(), pos, state);
        this.managedNode = GridHelper.createManagedNode(this, NodeListener.INSTANCE)
                .setFlags(GridFlags.DENSE_CAPACITY);
        this.managedNode.setIdlePowerUsage(ModConfig.INSTANCE.wirelessTransceiverIdlePower);
        this.managedNode.setTagName("labeled_wireless_node");
        this.managedNode.setInWorldNode(true);
        this.managedNode.setExposedOnSides(EnumSet.allOf(Direction.class));
        this.managedNode.setVisualRepresentation(ModItems.LABELED_WIRELESS_TRANSCEIVER.get().getDefaultInstance());
    }

    @Override
    public @Nullable IGridNode getGridNode(Direction dir) {
        return getGridNode();
    }

    @Override
    public ServerLevel getServerLevel() {
        Level lvl = super.getLevel();
        return lvl instanceof ServerLevel sl ? sl : null;
    }

    @Override
    public BlockPos getBlockPos() {
        return this.worldPosition;
    }

    @Override
    public IGridNode getGridNode() {
        return managedNode == null ? null : managedNode.getNode();
    }

    @Override
    public boolean isEndpointRemoved() {
        return super.isRemoved();
    }

    @Override
    public Component getDisplayName() {
        return Component.translatable("block.extendedae_plus.labeled_wireless_transceiver");
    }

    @Override
    public @Nullable AbstractContainerMenu createMenu(int id, Inventory inv, Player player) {
        return new LabeledWirelessTransceiverMenu(id, inv, this.worldPosition);
    }

    public void setPlacerId(@Nullable UUID placerId, @Nullable String placerName) {
        this.placerId = placerId;
        this.placerName = placerName;
        setChanged();
    }

    @Nullable
    public UUID getPlacerId() {
        return placerId;
    }

    @Nullable
    public String getPlacerName() {
        return placerName;
    }

    public long getFrequency() {
        return frequency;
    }

    @Nullable
    public String getLabelForDisplay() {
        return labelForDisplay;
    }

    public void applyLabel(@Nullable String rawLabel) {
        ServerLevel sl = getServerLevel();
        if (sl == null) return;

        // 先注销旧网络引用
        LabelNetworkRegistry.get(sl).unregister(this);

        var network = LabelNetworkRegistry.get(sl).register(sl, rawLabel, placerId, this);
        if (network == null) {
            clearLabel();
            return;
        }

        this.labelForDisplay = rawLabel;
        this.frequency = network.channel();
        this.labelLink.setTarget(network);
        updateState();
        setChanged();
    }

    public void clearLabel() {
        ServerLevel sl = getServerLevel();
        if (sl != null) {
            LabelNetworkRegistry.get(sl).unregister(this);
        }
        this.labelForDisplay = null;
        this.frequency = 0L;
        this.labelLink.clearTarget();
        updateState();
        setChanged();
    }

    public void refreshLabel(boolean ensureRegister) {
        ServerLevel sl = getServerLevel();
        if (sl == null) return;
        if (labelForDisplay == null || labelForDisplay.isEmpty()) {
            this.frequency = 0L;
            this.labelLink.clearTarget();
            updateState();
            return;
        }
        var registry = LabelNetworkRegistry.get(sl);
        var network = registry.getNetwork(sl, labelForDisplay, placerId);
        if (network == null && ensureRegister) {
            network = registry.register(sl, labelForDisplay, placerId, this);
        }
        if (network == null) {
            this.frequency = 0L;
            this.labelLink.clearTarget();
        } else {
            // 确保虚拟节点重建（网络从存档恢复时 managedNode 为空）
            network.ensureVirtualNode(sl);
            this.frequency = network.channel();
            this.labelLink.setTarget(network);
        }
        updateState();
        setChanged();
    }

    public void refreshLabel() {
        refreshLabel(false);
    }

    public void onRemoved() {
        this.beingRemoved = true;
        labelLink.onUnloadOrRemove();
        ServerLevel sl = getServerLevel();
        if (sl != null) {
            LabelNetworkRegistry.get(sl).unregister(this);
        }
        if (managedNode != null) {
            managedNode.destroy();
        }
    }

    public static void serverTick(Level level, BlockPos pos, BlockState state, LabeledWirelessTransceiverBlockEntity be) {
        if (!(level instanceof ServerLevel)) return;
        be.labelLink.updateStatus();
        be.updateState();
    }

    private void updateState() {
        if (this.level == null || this.level.isClientSide) return;
        if (this.beingRemoved || this.isRemoved()) return;
        BlockState currentState = this.getBlockState();
        if (!(currentState.getBlock() instanceof LabeledWirelessTransceiverBlock)) {
            return;
        }

        IGridNode node = this.getGridNode();
        boolean online = false;
        if (node != null && node.isActive()) {
            try {
                var grid = node.getGrid();
                online = grid != null && grid.getEnergyService().isNetworkPowered();
            } catch (Throwable ignored) {
                online = false;
            }
        }

        if (currentState.getValue(LabeledWirelessTransceiverBlock.STATE) != online) {
            this.level.setBlock(this.worldPosition, currentState.setValue(LabeledWirelessTransceiverBlock.STATE, online), 3);
        }
    }

    @Override
    public AECableType getCableConnectionType(Direction dir) {
        if (this.level == null) return AECableType.GLASS;
        var adjacentPos = this.worldPosition.relative(dir);
        if (!Objects.requireNonNull(this.getLevel()).hasChunkAt(adjacentPos)) return AECableType.GLASS;
        var adjacentHost = GridHelper.getNodeHost(this.getLevel(), adjacentPos);
        if (adjacentHost != null) {
            var t = adjacentHost.getCableConnectionType(dir.getOpposite());
            if (t != null) return t;
        }
        return AECableType.GLASS;
    }

    @Override
    public void onLoad() {
        super.onLoad();
        ServerLevel sl = getServerLevel();
        if (sl == null) return;
        GridHelper.onFirstTick(this, be -> {
            be.managedNode.create(be.getLevel(), be.getBlockPos());
            be.refreshLabel(true);
            be.labelLink.updateStatus();
            be.updateState();
        });
    }

    @Override
    public void saveAdditional(CompoundTag tag) {
        super.saveAdditional(tag);
        tag.putLong("frequency", frequency);
        if (labelForDisplay != null) {
            tag.putString("label", labelForDisplay);
        }
        if (placerId != null) {
            tag.putUUID("placerId", placerId);
        }
        if (placerName != null) {
            tag.putString("placerName", placerName);
        }
        if (managedNode != null) {
            managedNode.saveToNBT(tag);
        }
    }

    @Override
    public void loadTag(CompoundTag tag) {
        super.loadTag(tag);
        this.frequency = tag.getLong("frequency");
        if (tag.contains("label")) {
            this.labelForDisplay = tag.getString("label");
        } else {
            this.labelForDisplay = null;
        }
        if (tag.hasUUID("placerId")) {
            this.placerId = tag.getUUID("placerId");
        }
        if (tag.contains("placerName")) {
            this.placerName = tag.getString("placerName");
        }
        if (managedNode != null) {
            managedNode.loadFromNBT(tag);
        }
    }

    enum NodeListener implements IGridNodeListener<LabeledWirelessTransceiverBlockEntity> {
        INSTANCE;
        @Override
        public void onSaveChanges(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {
            host.setChanged();
        }
        @Override
        public void onStateChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node, State state) {
            host.updateState();
        }
        @Override
        public void onInWorldConnectionChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {
            host.updateState();
        }
        @Override
        public void onGridChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {
            host.updateState();
        }
        @Override
        public void onOwnerChanged(LabeledWirelessTransceiverBlockEntity host, IGridNode node) {}
    }
}
