package cc.carm.plugin.userprefix.conf.prefix;

import cc.carm.lib.easyplugin.gui.configuration.GUIActionConfiguration;
import cc.carm.lib.easyplugin.gui.configuration.GUIActionType;
import cc.carm.lib.easyplugin.utils.ColorParser;
import cc.carm.lib.easyplugin.utils.MessageUtils;
import cc.carm.lib.mineconfiguration.bukkit.value.item.PreparedItem;
import cc.carm.plugin.userprefix.Main;
import cc.carm.plugin.userprefix.manager.ServiceManager;
import org.bukkit.command.CommandSender;
import org.bukkit.entity.Player;
import org.bukkit.inventory.ItemStack;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Random;

public class PrefixConfig {

    static final Random RANDOM = new Random();

    protected final @NotNull String identifier;

    protected final @NotNull String name;
    protected final @NotNull List<String> content;
    protected final @NotNull List<String> description;

    protected final long period; // 内容变更周期，单位为毫秒

    protected final int weight;

    protected final @Nullable String permission;

    protected final @Nullable String group;

    protected final @NotNull List<GUIActionConfiguration> actions;

    protected final @NotNull ItemStack itemHasPermission;
    protected final @Nullable ItemStack itemNoPermission;
    protected final @Nullable ItemStack itemWhenUsing;

    public PrefixConfig(@NotNull String identifier, @NotNull String name,
                        @NotNull String content, int weight, @Nullable String permission,
                        @NotNull List<GUIActionConfiguration> actions,
                        @NotNull ItemStack itemHasPermission,
                        @Nullable ItemStack itemWhenUsing,
                        @Nullable ItemStack itemNoPermission) {
        this(
                identifier, name, new ArrayList<>(),
                content, weight, permission, actions,
                itemHasPermission, itemWhenUsing, itemNoPermission
        );
    }

    public PrefixConfig(@NotNull String identifier,
                        @NotNull String name, @NotNull List<String> description,
                        @NotNull String content, int weight, @Nullable String permission,
                        @NotNull List<GUIActionConfiguration> actions,
                        @NotNull ItemStack itemHasPermission,
                        @Nullable ItemStack itemWhenUsing,
                        @Nullable ItemStack itemNoPermission) {
        this(
                identifier, name, description,
                Collections.singletonList(content), 0, weight, permission, actions,
                itemHasPermission, itemWhenUsing, itemNoPermission
        );
    }

    public PrefixConfig(@NotNull String identifier,
                        @NotNull String name, @NotNull List<String> description,
                        @NotNull List<String> content, long period,
                        int weight, @Nullable String permission,
                        @NotNull List<GUIActionConfiguration> actions,
                        @NotNull ItemStack itemHasPermission,
                        @Nullable ItemStack itemWhenUsing,
                        @Nullable ItemStack itemNoPermission) {
        this(
                identifier, name, description,
                content, period, weight, permission, null, actions,
                itemHasPermission, itemWhenUsing, itemNoPermission
        );
    }

    public PrefixConfig(@NotNull String identifier,
                        @NotNull String name, @NotNull List<String> description,
                        @NotNull List<String> content, long period,
                        int weight, @Nullable String permission, @Nullable String group,
                        @NotNull List<GUIActionConfiguration> actions,
                        @NotNull ItemStack itemHasPermission,
                        @Nullable ItemStack itemWhenUsing,
                        @Nullable ItemStack itemNoPermission) {
        this.identifier = identifier;
        this.name = name;
        this.description = description;
        this.content = content;
        this.period = period;
        this.weight = weight;
        this.permission = permission;
        this.group = group;
        this.actions = actions;
        this.itemHasPermission = itemHasPermission;
        this.itemNoPermission = itemNoPermission;
        this.itemWhenUsing = itemWhenUsing;
    }

    @NotNull
    public String getIdentifier() {
        return identifier;
    }

    @NotNull
    public String getName() {
        return ColorParser.parse(name);
    }

    @NotNull
    public List<String> getDescription() {
        return ColorParser.parse(description);
    }

    @Nullable
    public String getGroup() {
        return group;
    }

    @NotNull
    public String getContent(CommandSender viewer) {
        if (content.isEmpty()) return "?";
        if (period < 0 || content.size() == 1) {
            return ColorParser.parse(MessageUtils.setPlaceholders(viewer, content.get(0)));
        }
        if (period == 0) {
            // PERIOD 为0时，随机返回一个内容
            int index = RANDOM.nextInt(content.size());
            return ColorParser.parse(MessageUtils.setPlaceholders(viewer, content.get(index)));
        } else {
            // 可变化的内容，则基于偏移量与时间戳计算目标index
            long curr = System.currentTimeMillis();
            long offset = curr % period; // 计算偏移量
            int index = (int) (offset / (period / content.size())); // 计算索引
            return ColorParser.parse(MessageUtils.setPlaceholders(viewer, content.get(index)));
        }
    }

    public int getWeight() {
        return weight;
    }

    @Nullable
    public String getPermission() {
        return permission;
    }

    @NotNull
    public ItemStack getItemHasPermission(@Nullable Player player) {
        return getItem(player, this.itemHasPermission);
    }

    @Nullable
    public ItemStack getItemNoPermission(@Nullable Player player) {
        return getItem(player, this.itemNoPermission);
    }

    @Nullable
    public ItemStack getItemWhenUsing(@Nullable Player player) {
        return getItem(player, this.itemWhenUsing);
    }

    @Contract("_,!null->!null")
    protected @Nullable ItemStack getItem(@Nullable Player player, @Nullable ItemStack item) {
        PreparedItem prepared = PreparedItem.of(item);

        prepared.parser((p, s) -> ColorParser.parse(MessageUtils.setPlaceholders(p, s)));

        if (!getDescription().isEmpty()) {
            prepared.insert("description", getDescription());
            prepared.placeholder("description", String.join("\n", getDescription()));
        }

        prepared.placeholder("name", getName());
        prepared.placeholder("identifier", getIdentifier());
        prepared.placeholder("content", getContent(player));
        prepared.placeholder("permission", getPermission());
        prepared.placeholder("weight", String.valueOf(getWeight()));
        prepared.placeholder("hasPermission", String.valueOf(checkPermission(player)));
        prepared.placeholder("public", String.valueOf(isPublic()));
        prepared.placeholder("visible", String.valueOf(isVisible(player)));

        return prepared.get(player);
    }


    public boolean isPublic() {
        return getPermission() == null;
    }

    public void executeActions(@NotNull Player player) {
        this.actions.forEach(action -> {
            if (action.getActionType() == GUIActionType.CONSOLE) { // 控制台执行命令必须在全局调度器中执行
                Main.getInstance().getFoliaScheduler().runGlobal(false, () -> action.executeAction(player));
            } else {
                action.executeAction(player);
            }
        });
    }

    public boolean isVisible(Player player) {
        return this.itemNoPermission != null || checkPermission(player);
    }

    /**
     * 判断某玩家是否有权限使用该前缀
     *
     * @param player 玩家
     * @return 若前缀标识不存在，则返回false；若前缀为默认前缀，或该前缀无权限，或玩家有该前缀的权限，则返回true。
     */
    public boolean checkPermission(Player player) {
        return permission == null || ServiceManager.hasPermission(player, permission);
    }

}
