package net.runelite.client.plugins.microbot.api.player.models;

import lombok.Getter;
import net.runelite.api.HeadIcon;
import net.runelite.api.MenuAction;
import net.runelite.api.Player;
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.actor.Rs2ActorModel;
import net.runelite.client.plugins.microbot.api.player.data.SalvagingAnimations;
import net.runelite.client.plugins.microbot.util.menu.NewMenuEntry;
import net.runelite.client.plugins.microbot.util.misc.Rs2UiHelper;

import java.util.Optional;

@Getter
public class Rs2PlayerModel extends Rs2ActorModel implements IEntity {

    private final Player player;

    public Rs2PlayerModel()
    {
        super(Microbot.getClient().getLocalPlayer());
        this.player = Microbot.getClient().getLocalPlayer();
    }

    public Rs2PlayerModel(final Player player)
    {
        super(player);
        this.player = player;
    }

    @Override
    public WorldPoint getWorldLocation()
    {
        return super.getWorldLocation();
    }

    @Override
    public int getId()
    {
        return player == null ? -1
                : Microbot.getClientThread().runOnClientThreadOptional(player::getId).orElse(-1);
    }


    @Override
    public boolean click() {
        return click("");
    }

    /**
     * Clicks a live player option using the client's current option/type arrays.
     *
     * @param action requested option, or blank for the first available option
     * @return true when a matching interaction was dispatched
     */
    @Override
    public boolean click(String action) {
        if (player == null) return false;

        Optional<NewMenuEntry> menuEntry = Microbot.getClientThread().runOnClientThreadOptional(() -> {
            String requestedAction = action == null ? "" : action.trim();
            boolean selectedWidgetAction = Microbot.getClient().isWidgetSelected()
                    && (requestedAction.isEmpty()
                    || requestedAction.equalsIgnoreCase("use")
                    || requestedAction.equalsIgnoreCase("cast"));
            if (selectedWidgetAction) {
                return playerMenuEntry("Use", MenuAction.WIDGET_TARGET_ON_PLAYER,
                        player.getId(), player.getName());
            }

            String[] options = Microbot.getClient().getPlayerOptions();
            int[] menuTypes = Microbot.getClient().getPlayerMenuTypes();
            int index = matchingPlayerOptionIndex(requestedAction, options, menuTypes);
            if (index < 0) return null;
            return playerMenuEntry(options[index], normalizedMenuAction(menuTypes[index]),
                    player.getId(), player.getName());
        });

        if (menuEntry.isEmpty()) return false;
        Microbot.status = (action == null || action.isBlank() ? "Interact" : action) + " player";
        Microbot.doInvoke(menuEntry.get(), Rs2UiHelper.getActorClickbox(player));
        return true;
    }

    private NewMenuEntry playerMenuEntry(String option, MenuAction menuAction,
                                         int playerId, String playerName) {
        return new NewMenuEntry()
                .param0(0)
                .param1(0)
                .opcode(menuAction.getId())
                .identifier(playerId)
                .itemId(-1)
                .target(playerName)
                .actor(player)
                .option(option);
    }

    static int matchingPlayerOptionIndex(String requestedAction, String[] options, int[] menuTypes) {
        if (options == null || menuTypes == null) return -1;
        String requested = requestedAction == null ? "" : requestedAction.trim();
        int optionCount = Math.min(options.length, menuTypes.length);
        for (int index = 0; index < optionCount; index++) {
            String option = options[index];
            if (option == null || option.isBlank()) continue;
            if (!requested.isEmpty() && !option.equalsIgnoreCase(requested)) continue;
            if (normalizedMenuAction(menuTypes[index]) != MenuAction.UNKNOWN) return index;
        }
        return -1;
    }

    private static MenuAction normalizedMenuAction(int menuTypeId) {
        int normalizedId = menuTypeId >= MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET
                ? menuTypeId - MenuAction.MENU_ACTION_DEPRIORITIZE_OFFSET
                : menuTypeId;
        return MenuAction.of(normalizedId);
    }

    // Sailing stuff
    public boolean isSalvaging() {
        int anim = getAnimation();
        for (int id : SalvagingAnimations.SALVAGING_ANIMATIONS) {
            if (anim == id) {
                return true;
            }
        }
        return false;
    }

    /**
     * Checks if this player is in your friends list.
     *
     * @return true if the player is a friend
     */
    public boolean isFriend() {
        return Microbot.getClientThread().runOnClientThreadOptional(player::isFriend).orElse(false);
    }

    /**
     * Checks if this player is a member of your clan.
     *
     * @return true if the player is a clan member
     */
    public boolean isClanMember() {
        return Microbot.getClientThread().runOnClientThreadOptional(player::isClanMember).orElse(false);
    }

    /**
     * Checks if this player is in your friends chat.
     *
     * @return true if the player is in the friends chat channel
     */
    public boolean isFriendsChatMember() {
        return Microbot.getClientThread().runOnClientThreadOptional(player::isFriendsChatMember).orElse(false);
    }

    /**
     * Gets the skull icon shown above this player's head, or -1 if none.
     *
     * @return the skull icon id, or -1
     */
    public int getSkullIcon() {
        return Microbot.getClientThread().runOnClientThreadOptional(player::getSkullIcon).orElse(-1);
    }

    /**
     * Gets the overhead prayer icon shown above this player's head.
     *
     * @return the overhead HeadIcon, or null if none
     */
    public HeadIcon getOverheadIcon() {
        return Microbot.getClientThread().runOnClientThreadOptional(player::getOverheadIcon).orElse(null);
    }
}
