package pers.neige.neigeitems.action.evaluator.impl;

import lombok.Getter;
import lombok.NonNull;
import lombok.ToString;
import lombok.val;
import org.jetbrains.annotations.Contract;
import org.jetbrains.annotations.Nullable;
import pers.neige.neigeitems.action.ActionContext;
import pers.neige.neigeitems.action.ScriptWithSource;
import pers.neige.neigeitems.action.evaluator.Evaluator;
import pers.neige.neigeitems.action.evaluator.EvaluatorConverter;
import pers.neige.neigeitems.manager.BaseActionManager;
import pers.neige.neigeitems.utils.StringUtils;

import javax.script.Compilable;
import javax.script.ScriptException;
import java.util.logging.Level;

@Getter
@ToString(callSuper = true)
public class JsEvaluator<T> extends Evaluator<T> {
    private final @Nullable ScriptWithSource script;
    private final @NonNull EvaluatorConverter<T> converter;

    public JsEvaluator(@NonNull BaseActionManager manager, @NonNull Class<T> type, @Nullable String script) {
        this(manager, type, script, new EvaluatorConverter<>(type));
    }

    public JsEvaluator(
        @NonNull BaseActionManager manager,
        @NonNull Class<T> type,
        @Nullable String script,
        @NonNull EvaluatorConverter<T> converter
    ) {
        super(manager, type);
        try {
            this.script = ScriptWithSource.compile((Compilable) manager.getEngine(), script);
        } catch (ScriptException e) {
            throw new RuntimeException(e);
        }
        this.converter = converter;
    }

    public @Nullable T cast(@NonNull Object result) {
        return converter.convert(result);
    }

    @Override
    @Contract("_, !null -> !null")
    public @Nullable T getOrDefault(@NonNull ActionContext context, @Nullable T def) {
        if (this.script == null) return def;
        Object evalResult;
        try {
            evalResult = this.script.eval(context.getBindings());
            if (evalResult == null) return def;
        } catch (Throwable error) {
            val lines = this.script.getSource().split("\n");
            for (int i = 0; i < lines.length; i++) {
                val conditionLine = lines[i];
                lines[i] = (i + 1) + ". " + conditionLine;
            }
            manager.getPlugin().getLogger().log(Level.WARNING, this.getClass().getSimpleName() + "解析异常, 脚本内容如下:\n" + StringUtils.joinToString(lines, "\n", 0), error);
            return def;
        }
        val result = cast(evalResult);
        return result == null ? def : result;
    }
}
