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

import ch.njol.skript.classes.Changer.ChangeMode;
import ch.njol.skript.lang.Expression;
import ch.njol.skript.lang.SkriptParser.ParseResult;
import ch.njol.skript.lang.util.SimpleExpression;
import ch.njol.skript.util.Color;
import ch.njol.skript.util.SkriptColor;
import ch.njol.util.Kleenean;
import ch.njol.util.coll.CollectionUtils;
import com.github.shanebeee.skr.Registration;
import com.shanebeestudios.skbee.api.util.BossBarUtils;
import org.bukkit.boss.BarColor;
import org.bukkit.boss.BarFlag;
import org.bukkit.boss.BarStyle;
import org.bukkit.boss.BossBar;
import org.bukkit.entity.Player;
import org.bukkit.event.Event;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

public class ExprBossBarProperties extends SimpleExpression<Object> {

    public static void register(Registration reg) {
        reg.newCombinedExpression(ExprBossBarProperties.class, Object.class,
                "[boss[ ]]bar players of %bossbar%",
                "[boss[ ]]bar (color|colour) of %bossbar%",
                "[boss[ ]]bar style of %bossbar%",
                "[boss[ ]]bar title of %bossbar%",
                "[boss[ ]]bar progress of %bossbar%",
                "[boss[ ]]bar flag %bossbarflag% of %bossbar%",
                "[boss[ ]]bar visibility of %bossbar%")
            .name("BossBar - Properties")
            .description("Represents the properties of a BossBar that can be changed.",
                "Progress of a bar is a number from 0-100.")
            .examples("set {_players::*} to bar players of {_bar}",
                "set bar color of {_bar} to blue",
                "set bar style of {_bar} to segmented 20",
                "set bar title of {_bar} to \"Le-Title\"",
                "reset bar title of {_bar}",
                "set bar progress of {_bar} to 100",
                "set bar flag darken sky of {_bar} to true")
            .since("2.14.1")
            .register();
    }

    private static final int PLAYERS = 0;
    private static final int BAR_COLOR = 1;
    private static final int BAR_STYLE = 2;
    private static final int BAR_TITLE = 3;
    private static final int BAR_PROGRESS = 4;
    private static final int BAR_FLAG = 5;
    private static final int BAR_VISIBILITY = 6;
    private int pattern;
    private Expression<BossBar> bossBar;
    private Expression<BarFlag> barFlag;

    @SuppressWarnings({"NullableProblems", "unchecked"})
    @Override
    public boolean init(Expression<?>[] exprs, int matchedPattern, Kleenean isDelayed, ParseResult parseResult) {
        pattern = matchedPattern;
        bossBar = (Expression<BossBar>) exprs[pattern == 5 ? 1 : 0];
        barFlag = pattern == 5 ? (Expression<BarFlag>) exprs[0] : null;
        return true;
    }

    @Override
    protected Object @Nullable [] get(Event event) {
        BossBar bossBar = this.bossBar.getSingle(event);
        if (bossBar == null) return null;

        switch (this.pattern) {
            case PLAYERS -> {
                return bossBar.getPlayers().toArray(new Player[0]);
            }
            case BAR_COLOR -> {
                return new SkriptColor[]{BossBarUtils.getSkriptColor(bossBar.getColor())};
            }
            case BAR_STYLE -> {
                return new BarStyle[]{bossBar.getStyle()};
            }
            case BAR_TITLE -> {
                return new String[]{bossBar.getTitle()};
            }
            case BAR_PROGRESS -> {
                return new Number[]{(bossBar.getProgress() * 100)};
            }
            case BAR_FLAG -> {
                BarFlag barFlag = this.barFlag.getSingle(event);
                if (barFlag == null) return null;
                return new Boolean[]{bossBar.hasFlag(barFlag)};
            }
            case BAR_VISIBILITY -> {
                return new Boolean[]{bossBar.isVisible()};
            }
        }
        return null;
    }

    @SuppressWarnings("NullableProblems")
    @Override
    public Class<?> @Nullable [] acceptChange(ChangeMode mode) {
        if (this.pattern == PLAYERS) {
            if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.SET || mode == ChangeMode.RESET) {
                return CollectionUtils.array(Player[].class);
            }
        } else if (this.pattern == BAR_COLOR && mode == ChangeMode.SET) {
            return CollectionUtils.array(Color.class, BarColor.class);
        } else if (this.pattern == BAR_STYLE && mode == ChangeMode.SET) {
            return CollectionUtils.array(BarStyle.class);
        } else if (this.pattern == BAR_TITLE && (mode == ChangeMode.SET || mode == ChangeMode.RESET || mode == ChangeMode.DELETE)) {
            return CollectionUtils.array(String.class);
        } else if (this.pattern == BAR_PROGRESS) {
            if (mode == ChangeMode.ADD || mode == ChangeMode.REMOVE || mode == ChangeMode.SET || mode == ChangeMode.RESET) {
                return CollectionUtils.array(Number.class);
            }
        } else if (this.pattern == BAR_FLAG) {
            if (mode == ChangeMode.SET || mode == ChangeMode.REMOVE) {
                return CollectionUtils.array(Boolean.class);
            }
        } else if (this.pattern == BAR_VISIBILITY) {
            if (mode == ChangeMode.SET) {
                return CollectionUtils.array(Boolean.class);
            }
        }
        return null;
    }

    @SuppressWarnings("ConstantConditions")
    @Override
    public void change(Event event, @Nullable Object[] delta, ChangeMode mode) {
        Object object = delta != null ? delta[0] : null;
        BossBar bossBar = this.bossBar.getSingle(event);
        if (bossBar == null) return;

        if (this.pattern == PLAYERS) {
            if (delta instanceof Player[] players) {
                if (mode == ChangeMode.SET || mode == ChangeMode.RESET) {
                    bossBar.removeAll();
                }
                if (mode == ChangeMode.ADD || mode == ChangeMode.SET) {
                    for (Player player : players) {
                        bossBar.addPlayer(player);
                    }
                } else if (mode == ChangeMode.REMOVE) {
                    for (Player player : players) {
                        bossBar.removePlayer(player);
                    }
                }

            }
        } else if (this.pattern == BAR_COLOR && mode == ChangeMode.SET) {
            if (object instanceof SkriptColor skriptColor) {
                BarColor bossBarColor = BossBarUtils.getBossBarColor(skriptColor);
                bossBar.setColor(bossBarColor);
            } else if (object instanceof BarColor barColor) {
                bossBar.setColor(barColor);
            }
        } else if (this.pattern == BAR_STYLE && mode == ChangeMode.SET) {
            if (object instanceof BarStyle barStyle) {
                bossBar.setStyle(barStyle);
            }
        } else if (this.pattern == BAR_TITLE) {
            if (mode == ChangeMode.SET && object instanceof String title) {
                bossBar.setTitle(title);
            } else if (mode == ChangeMode.RESET || mode == ChangeMode.DELETE) {
                bossBar.setTitle(null);
            }
        } else if (this.pattern == BAR_PROGRESS) {
            if (mode == ChangeMode.RESET) {
                bossBar.setProgress(1.0);
            } else if (object instanceof Number number) {
                double newProgress = 0;
                double oldProgress = bossBar.getProgress();
                float progress = number.floatValue() / 100;
                if (mode == ChangeMode.SET) {
                    newProgress = progress;
                } else if (mode == ChangeMode.ADD) {
                    newProgress = oldProgress + progress;
                } else if (mode == ChangeMode.REMOVE) {
                    newProgress = oldProgress - progress;
                }

                newProgress = Math.clamp((float) newProgress, 0, 1);
                if (Double.isNaN(newProgress)) newProgress = 0;
                bossBar.setProgress(newProgress);
            }
        } else if (this.pattern == BAR_FLAG) {
            BarFlag barFlag = this.barFlag.getSingle(event);
            if (barFlag == null) return;

            if (mode == ChangeMode.SET && object instanceof Boolean setter) {
                if (setter) {
                    bossBar.addFlag(barFlag);
                } else {
                    bossBar.removeFlag(barFlag);
                }
            } else if (mode == ChangeMode.REMOVE) {
                bossBar.removeFlag(barFlag);
            }
        } else if (this.pattern == BAR_VISIBILITY && object instanceof Boolean isVisible) {
            bossBar.setVisible(isVisible);
        }
    }

    @Override
    public boolean isSingle() {
        return this.pattern != 0;
    }

    @Override
    public @NotNull Class<?> getReturnType() {
        return switch (this.pattern) {
            case PLAYERS -> Player.class;
            case BAR_COLOR -> SkriptColor.class;
            case BAR_STYLE -> BarStyle.class;
            case BAR_TITLE -> String.class;
            case BAR_PROGRESS -> Number.class;
            case BAR_FLAG, BAR_VISIBILITY -> Boolean.class;
            default -> throw new IllegalStateException("Unexpected value: " + this.pattern);
        };
    }

    @Override
    public @NotNull String toString(Event e, boolean d) {
        String prop = switch (this.pattern) {
            case PLAYERS -> "bar players";
            case BAR_COLOR -> "bar color";
            case BAR_STYLE -> "bar style";
            case BAR_TITLE -> "bar title";
            case BAR_PROGRESS -> "bar progress";
            case BAR_FLAG -> "bar flag " + this.barFlag.toString(e, d);
            case BAR_VISIBILITY -> "bar visibility";
            default -> "null?!?!";
        };
        return prop + " of boss bar " + this.bossBar.toString(e, d);
    }

}
