package net.miarma.hystuff.commands.teleport;

import com.hypixel.hytale.component.Ref;
import com.hypixel.hytale.component.Store;
import com.hypixel.hytale.math.vector.Transform;
import com.hypixel.hytale.server.core.Message;
import com.hypixel.hytale.server.core.command.system.CommandContext;
import com.hypixel.hytale.server.core.command.system.arguments.system.OptionalArg;
import com.hypixel.hytale.server.core.command.system.arguments.system.RequiredArg;
import com.hypixel.hytale.server.core.command.system.arguments.types.ArgTypes;
import com.hypixel.hytale.server.core.command.system.basecommands.AbstractPlayerCommand;
import com.hypixel.hytale.server.core.modules.entity.component.TransformComponent;
import com.hypixel.hytale.server.core.modules.entity.teleport.Teleport;
import com.hypixel.hytale.server.core.universe.PlayerRef;
import com.hypixel.hytale.server.core.universe.Universe;
import com.hypixel.hytale.server.core.universe.world.World;
import com.hypixel.hytale.server.core.universe.world.storage.EntityStore;
import net.miarma.hystuff.HyStuff;
import net.miarma.hystuff.component.TpaRequestComponent;
import org.checkerframework.checker.nullness.compatqual.NonNullDecl;

import java.util.UUID;

public class TpAcceptCommand extends AbstractPlayerCommand {

    private final OptionalArg<PlayerRef> targetPlayerArg;

    public TpAcceptCommand() {
        super("tpaccept", "Accepts a teleport request");
        targetPlayerArg = this.withOptionalArg("targetPlayer",
                "Player you want to accept teleport from", ArgTypes.PLAYER_REF);
    }

    @Override
    protected boolean canGeneratePermission() {
        return false;
    }

    @Override
    protected void execute(@NonNullDecl CommandContext ctx,
                           @NonNullDecl Store<EntityStore> store,
                           @NonNullDecl Ref<EntityStore> ref,
                           @NonNullDecl PlayerRef playerRef,
                           @NonNullDecl World world) {

        PlayerRef targetPlayerRef = this.targetPlayerArg.get(ctx);

        TpaRequestComponent tpaRequestComp = store.getComponent(ref, HyStuff.TPA_REQUEST_COMPONENT_TYPE);
        if (tpaRequestComp == null) {
            ctx.sendMessage(Message.raw("You don't have pending teleport requests."));
            return;
        }

        UUID requesterUuid = tpaRequestComp.getRequester();
        if (requesterUuid == null) {
            ctx.sendMessage(Message.raw("Teleport request is corrupted."));
            store.removeComponent(ref, HyStuff.TPA_REQUEST_COMPONENT_TYPE);
            return;
        }

        if (targetPlayerRef != null && !targetPlayerRef.getUuid().equals(requesterUuid)) {
            ctx.sendMessage(Message.raw("You don't have a teleport request from that player."));
            return;
        }

        PlayerRef requester = Universe.get().getPlayer(requesterUuid);
        if (requester == null || !requester.isValid()) {
            ctx.sendMessage(Message.raw("The requester is no longer online."));
            store.removeComponent(ref, HyStuff.TPA_REQUEST_COMPONENT_TYPE);
            return;
        }

        if (tpaRequestComp.getType() == TpaRequestComponent.RequestType.TPA_HERE) {
            teleportRequesterToReceiver(store, ref, requester, playerRef, world, ctx);
        } else {
            teleportReceiverToRequester(store, ref, requester, playerRef, world, ctx);
        }

        store.removeComponent(ref, HyStuff.TPA_REQUEST_COMPONENT_TYPE);
    }

    private void teleportReceiverToRequester(Store<EntityStore> store,
                                             Ref<EntityStore> ref,
                                             PlayerRef requester,
                                             PlayerRef receiver,
                                             World world,
                                             CommandContext ctx) {

        Ref<EntityStore> requesterRef = requester.getReference();
        if (requesterRef == null || !requesterRef.isValid()) {
            ctx.sendMessage(Message.raw("The requester is no longer in the world."));
            return;
        }

        Store<EntityStore> requesterStore = requesterRef.getStore();
        World requesterWorld = requesterStore.getExternalData().getWorld();

        requesterWorld.execute(() -> {
            TransformComponent requesterTransform = requesterStore.getComponent(requesterRef,
                    TransformComponent.getComponentType());
            assert requesterTransform != null;

            Transform targetTransform = new Transform(
                    requesterTransform.getPosition().clone(),
                    requesterTransform.getRotation().clone()
            );

            world.execute(() -> {
                Teleport teleportComponent = Teleport.createForPlayer(requesterWorld, targetTransform);
                store.addComponent(ref, Teleport.getComponentType(), teleportComponent);
                ctx.sendMessage(Message.raw("Teleport request accepted!"));
            });
        });
    }

    private void teleportRequesterToReceiver(Store<EntityStore> store,
                                             Ref<EntityStore> ref,
                                             PlayerRef requester,
                                             PlayerRef receiver,
                                             World world,
                                             CommandContext ctx) {

        Ref<EntityStore> receiverRef = receiver.getReference();
        if (receiverRef == null || !receiverRef.isValid()) {
            ctx.sendMessage(Message.raw("The receiver is no longer in the world."));
            return;
        }

        Store<EntityStore> receiverStore = receiverRef.getStore();
        World receiverWorld = receiverStore.getExternalData().getWorld();

        receiverWorld.execute(() -> {
            TransformComponent receiverTransform = receiverStore.getComponent(receiverRef,
                    TransformComponent.getComponentType());
            assert receiverTransform != null;

            Transform targetTransform = new Transform(
                    receiverTransform.getPosition().clone(),
                    receiverTransform.getRotation().clone()
            );

            world.execute(() -> {
                Teleport teleportComponent = Teleport.createForPlayer(receiverWorld, targetTransform);
                store.addComponent(requester.getReference(), Teleport.getComponentType(), teleportComponent);
                ctx.sendMessage(Message.raw("Teleport request accepted!"));
            });
        });
    }
}
