package com.shanebeestudios.skbee.elements.scoreboard.expressions;

import ch.njol.skript.Skript;
import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import com.github.shanebeee.skr.Registration;
import com.shanebeestudios.skbee.api.scoreboard.TeamUtils;
import com.shanebeestudios.skbee.api.skript.base.SimpleExpression;
import org.bukkit.OfflinePlayer;
import org.bukkit.entity.Entity;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.bukkit.scoreboard.Team;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class ExprTeamEntries extends SimpleExpression<Object> {

    public static void register(Registration reg) {
        reg.newSimpleExpression(ExprTeamEntries.class, Object.class,
                "(:string|entity) team entries of %teams%",
                "%teams%'[s] (:string|entity) team entries",
                "[all] team entries [string:as strings] of %team%",
                "[all] team entries of %team% [string:as strings]",
                "%team%'[s] team entries [string:as strings]")
            .name("Team - Entries")
            .description("Get the entries of a team. Entries can be entities/players or strings.",
                "**NOTE**: When returning as entities, if the entity isn't currently loaded in a world it won't return.",
                "OfflinePlayers will also not return. Use strings intead.",
                "**NOTE**: When returning as strings this will return the list how Minecraft stores it, player names and entity UUIDs.",
                "**NOTE**: adding/removing to/from team entries is now deprecated. Please directly add/remove to/from the team itself.",
                "See Team type docs for more info!")
            .examples("set {_team} to team named \"my-team\"",
                "clear team entries of {_team}",
                "kill team entries of team named \"mob-team\"",
                "set {_entities::*} to team entries of team named \"mobs\"",
                "set {_strings::*} to team entries as strings of team named \"mobs\"")
            .since("1.16.0, 2.10.0 (strings)")
            .register();
    }

    private Expression<Team> team;
    private boolean strings;

    @SuppressWarnings("unchecked")
    @Override
    public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
        this.team = (Expression<Team>) exprs[0];
        this.strings = parseResult.hasTag("string");
        return true;
    }

    @Override
    protected Object[] get(Event event) {
        Team team = this.team.getSingle(event);
        if (team != null) {
            if (this.strings) {
                return team.getEntries().toArray(new String[0]);
            } else {
                return TeamUtils.getEntries(team).toArray(new Entity[0]);
            }
        } else {
            return null;
        }
    }

    @Nullable
    @Override
    public Class<?> @Nullable [] acceptChange(ChangeMode mode) {
        switch (mode) {
            case ADD, REMOVE -> {
                // TODO Deprecated 2.11.0
                Skript.warning("You can now add/remove entities/strings to/from teams directly without this expression. " +
                    "ex: 'add player to team named \"a-team\"'");
                return CollectionUtils.array(OfflinePlayer[].class, Player[].class, Entity[].class, String[].class);
            }
            case DELETE -> {
                return CollectionUtils.array();
            }
            default -> {
                return null;
            }
        }
    }

    @Override
    public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
        Team team = this.team.getSingle(event);
        if (team != null) {
            if (mode == ChangeMode.DELETE) {
                team.getEntries().forEach(team::removeEntry);
                return;
            }
            for (Object object : delta) {
                String entry = null;
                if (object instanceof OfflinePlayer player) {
                    entry = player.getName();
                } else if (object instanceof Entity entity) {
                    entry = entity.getUniqueId().toString();
                } else if (object instanceof String string) {
                    entry = string;
                }
                if (entry == null) {
                    continue;
                }

                if (mode == ChangeMode.ADD) {
                    team.addEntry(entry);
                } else {
                    team.removeEntry(entry);
                }
            }
        }
    }

    @Override
    public boolean isSingle() {
        return false;
    }

    @Override
    public @NotNull Class<?> getReturnType() {
        return this.strings ? String.class : Entity.class;
    }

    @Override
    public @NotNull String toString(Event e, boolean d) {
        String string = this.strings ? "string" : "entity";
        return string + " entries of " + this.team.toString(e, d);
    }

}
