package net.runelite.client.plugins.microbot.api.tileobject.models;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import net.runelite.api.*;
import net.runelite.api.Point;
import net.runelite.api.coords.LocalPoint;
import net.runelite.api.coords.WorldPoint;
import net.runelite.client.plugins.microbot.Microbot;
import net.runelite.client.plugins.microbot.api.IEntity;
import net.runelite.client.plugins.microbot.api.boat.Rs2BoatCache;
import net.runelite.client.plugins.microbot.util.camera.Rs2Camera;
import net.runelite.client.plugins.microbot.util.equipment.Rs2Equipment;
import net.runelite.client.plugins.microbot.util.gameobject.ObjectActionSelection;
import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry;
import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.awt.*;

import static net.runelite.client.plugins.microbot.util.Global.sleepUntil;

@Slf4j
public class Rs2TileObjectModel implements TileObject, IEntity {

    static int actionIndex(String[] actions, String requested) {
        return ObjectActionSelection.index(actions, requested);
    }

    public Rs2TileObjectModel(GameObject gameObject) {
        this.tileObject = gameObject;
        this.tileObjectType = TileObjectType.GAME;
    }

    public Rs2TileObjectModel(DecorativeObject tileObject) {
        this.tileObject = tileObject;
        this.tileObjectType = TileObjectType.DECORATIVE;
    }

    public Rs2TileObjectModel(WallObject tileObject) {
        this.tileObject = tileObject;
        this.tileObjectType = TileObjectType.WALL;
    }

    public Rs2TileObjectModel(GroundObject tileObject) {
        this.tileObject = tileObject;
        this.tileObjectType = TileObjectType.GROUND;
    }

    public Rs2TileObjectModel(TileObject tileObject) {
        this.tileObject = tileObject;
        this.tileObjectType = TileObjectType.GENERIC;
    }

    @Getter
    private final TileObjectType tileObjectType;
    /** Underlying client-owned object; obtain a fresh model after scene changes. */
    @Getter
    private final TileObject tileObject;
    private String[] actions;


    @Override
    public long getHash() {
        return tileObject.getHash();
    }

    @Override
    public int getX() {
        return tileObject.getX();
    }

    @Override
    public int getY() {
        return tileObject.getY();
    }

    @Override
    public int getZ() {
        return tileObject.getZ();
    }

    @Override
    public int getPlane() {
        return tileObject.getPlane();
    }

    @Override
    public WorldView getWorldView() {
        return Microbot.getClientThread().runOnClientThreadOptional(tileObject::getWorldView).orElse(null);
    }

    public int getId() {
        return tileObject.getId();
    }

    @Override
    public @NotNull WorldPoint getWorldLocation() {
        return Microbot.getClientThread().invoke(() -> {
            WorldPoint worldLocation = tileObject.getWorldLocation();
            if (!(tileObject instanceof GameObject)) {
                return worldLocation;
            }

            GameObject gameObject = (GameObject) tileObject;
            WorldView worldView = tileObject.getWorldView();
            Point sceneMin = gameObject.getSceneMinLocation();
            if (worldView == null || sceneMin == null) {
                return worldLocation;
            }

            return WorldPoint.fromScene(worldView, sceneMin.getX(), sceneMin.getY(), worldView.getPlane());
        });
    }

    public String getName() {
        return Microbot.getClientThread().invoke(() -> {
            ObjectComposition composition = Microbot.getClient().getObjectDefinition(tileObject.getId());
            if (composition.getImpostorIds() != null) {
                composition = composition.getImpostor();
            }
            if (composition == null)
                return null;
            return Rs2UiHelper.stripColTags(composition.getName());
        });
    }

    @Override
    public @NotNull LocalPoint getLocalLocation() {
        return tileObject.getLocalLocation();
    }

    @Override
    public @Nullable Point getCanvasLocation() {
        return tileObject.getCanvasLocation();
    }

    @Override
    public @Nullable Point getCanvasLocation(int zOffset) {
        return tileObject.getCanvasLocation();
    }

    @Override
    public @Nullable Polygon getCanvasTilePoly() {
        return tileObject.getCanvasTilePoly();
    }

    @Override
    public @Nullable Point getCanvasTextLocation(Graphics2D graphics, String text, int zOffset) {
        return tileObject.getCanvasTextLocation(graphics, text, zOffset);
    }

    @Override
    public @Nullable Point getMinimapLocation() {
        return tileObject.getMinimapLocation();
    }

    @Override
    public @Nullable Shape getClickbox() {
        return tileObject.getClickbox();
    }

    @Override
    public @Nullable String getOpOverride(int index) {
        return tileObject.getOpOverride(index);
    }

    @Override
    public boolean isOpShown(int index) {
        return tileObject.isOpShown(index);
    }

    public ObjectComposition getObjectComposition() {
        return Microbot.getClientThread().invoke(() -> {
            ObjectComposition composition = Microbot.getClient().getObjectDefinition(tileObject.getId());
            if (composition.getImpostorIds() != null) {
                composition = composition.getImpostor();
            }
            return composition;
        });
    }

    /** True only when the live object exposes the requested menu action. */
    public boolean hasAction(String requested) {
        return Microbot.getClientThread().runOnClientThreadOptional(() -> {
            ObjectComposition composition = Microbot.getClient().getObjectDefinition(tileObject.getId());
            if (composition != null && composition.getImpostorIds() != null) {
                composition = composition.getImpostor();
            }
            return composition != null && actionIndex(composition.getActions(), requested) >= 0;
        }).orElse(false);
    }

    @Override
    public boolean isReachable() {
        WorldView objectWorldView = getWorldView();
        if (objectWorldView == null) {
            return false;
        }

        WorldView playerWorldView = Microbot.getClientThread().runOnClientThreadOptional(() -> {
            Player player = Microbot.getClient().getLocalPlayer();
            return player != null ? player.getWorldView() : null;
        }).orElse(null);

        if (playerWorldView == null) {
            return false;
        }

        if (objectWorldView.getId() == playerWorldView.getId()) {
            return true;
        }

        return IEntity.super.isReachable();
    }

    public boolean click() {
        return click("");
    }

    /**
     * Clicks on the specified tile object with no specific action.
     * Delegates to Rs2GameObject.clickObject.
     *
     * @param action the action to perform (e.g., "Open", "Climb")
     * @return true if the interaction was successful, false otherwise
     */
    public boolean click(String action) {
        try {

            int param0;
            int param1;
            MenuAction menuAction = MenuAction.WALK;


            Microbot.status = action + " " + getName();

            if (getTileObjectType() == TileObjectType.GAME) {
                GameObject obj = (GameObject) tileObject;
                if (obj.sizeX() > 1) {
                    param0 = obj.getLocalLocation().getSceneX() - obj.sizeX() / 2;
                } else {
                    param0 = obj.getLocalLocation().getSceneX();
                }

                if (obj.sizeY() > 1) {
                    param1 = obj.getLocalLocation().getSceneY() - obj.sizeY() / 2;
                } else {
                    param1 = obj.getLocalLocation().getSceneY();
                }
            } else {
                // Default objects like walls, groundobjects, decorationobjects etc...
                param0 = getLocalLocation().getSceneX();
                param1 = getLocalLocation().getSceneY();
            }


            boolean targeting = Microbot.getClient().isWidgetSelected();
            int index = 0;
            String objName = "";
            if (!targeting) {
                var objComp = getObjectComposition();
                if (objComp == null) return false;
                String[] actions;
                if (objComp.getImpostorIds() != null && objComp.getImpostor() != null) {
                    actions = objComp.getImpostor().getActions();
                } else {
                    actions = objComp.getActions();
                }

                index = actionIndex(actions, action);
                if (index < 0) {
                    log.debug("Failed to interact with object {} - action '{}' not found", getId(), action);
                    return false;
                }

                objName = objComp.getName();

                // both hands must be free before using MINECART
                if (objComp.getName().toLowerCase().contains("train cart")) {
                    Rs2Equipment.unEquip(EquipmentInventorySlot.WEAPON);
                    Rs2Equipment.unEquip(EquipmentInventorySlot.SHIELD);
                    sleepUntil(() -> Rs2Equipment.get(EquipmentInventorySlot.WEAPON) == null && Rs2Equipment.get(EquipmentInventorySlot.SHIELD) == null);
                }
            }

            if (targeting) {
                menuAction = MenuAction.WIDGET_TARGET_ON_GAME_OBJECT;
            } else if (index == 0) {
                menuAction = MenuAction.GAME_OBJECT_FIRST_OPTION;
            } else if (index == 1) {
                menuAction = MenuAction.GAME_OBJECT_SECOND_OPTION;
            } else if (index == 2) {
                menuAction = MenuAction.GAME_OBJECT_THIRD_OPTION;
            } else if (index == 3) {
                menuAction = MenuAction.GAME_OBJECT_FOURTH_OPTION;
            } else if (index == 4) {
                menuAction = MenuAction.GAME_OBJECT_FIFTH_OPTION;
            }

            if (!Rs2Camera.isTileOnScreen(getLocalLocation())) {
                Rs2Camera.turnTo(tileObject);
            }


            Microbot.doInvoke(new NewMenuEntry()
                            .param0(param0)
                            .param1(param1)
                            .opcode(menuAction.getId())
                            .identifier(getId())
                            .itemId(-1)
                            .option(action)
                            .target(objName)
                            .setWorldViewId(getWorldView().getId())
                            .gameObject(tileObject)
                    ,
                    Rs2UiHelper.getObjectClickbox(tileObject));
// MenuEntryImpl(getOption=Use, getTarget=Barrier, getIdentifier=43700, getType=GAME_OBJECT_THIRD_OPTION, getParam0=53, getParam1=51, getItemId=-1, isForceLeftClick=true, getWorldViewId=-1, isDeprioritized=false)
            //Rs2Reflection.invokeMenu(param0, param1, menuAction.getId(), object.getId(),-1, "", "", -1, -1);

        } catch (Exception ex) {
            log.error("Failed to interact with object: ", ex);
        }

        return true;
    }

}
