package com.xfestudio.xfeservermanager.core.trigger;

import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/** Deterministically compiles a legacy visual or XFE Script v1 program to V2 in memory. */
public final class TriggerLegacyV2Compiler {
    private TriggerLegacyV2Compiler() { }

    public static TriggerProgramV2 compile(TriggerDefinition definition) {
        TriggerDefinition.Program legacy = definition.executableProgram();
        List<TriggerProgramV2.VariableDeclaration> declarations = legacy.event().variables().stream()
                .map(variable -> new TriggerProgramV2.VariableDeclaration(
                        id(definition.id(), "variable/" + variable.contextKey()),
                        variable.name(), variable.type(),
                        new TriggerExpression(
                                id(definition.id(), "variable/" + variable.contextKey() + "/initial"),
                                TriggerExpression.Kind.LITERAL, variable.type(),
                                TriggerValueTypes.parseVariable(variable.type(), variable.initialValue()),
                                "", List.of()),
                        variable.visibility(), variable.storage(),
                        TriggerProgramV2.Lifetime.valueOf(variable.lifetime().toUpperCase(java.util.Locale.ROOT)),
                        variable.ttlSeconds()))
                .toList();
        List<TriggerProgramV2.Statement> body = actions(definition.id(), legacy.actions(), "action");
        if (!legacy.conditions().isEmpty()) {
            List<TriggerExpression> conditions = new ArrayList<>();
            for (int index = 0; index < legacy.conditions().size(); index++) {
                TriggerDefinition.Condition condition = legacy.conditions().get(index);
                Map<String, Object> literal = new LinkedHashMap<>();
                literal.put("field", condition.field());
                literal.put("operator", condition.operator());
                literal.put("value", condition.value());
                conditions.add(new TriggerExpression(
                        id(definition.id(), "condition/" + index),
                        TriggerExpression.Kind.FUNCTION, "bool", null, "legacy_condition",
                        List.of(new TriggerExpression(
                                id(definition.id(), "condition/" + index + "/literal"),
                                TriggerExpression.Kind.LITERAL, "record", literal, "", List.of()))));
            }
            TriggerExpression combined = combine(definition.id(), legacy.conditionMode(), conditions);
            body = List.of(new TriggerProgramV2.Statement(
                    id(definition.id(), "root-condition"), TriggerProgramV2.StatementKind.IF, "",
                    Map.of(), combined, List.of(), body, List.of()));
        }
        return new TriggerProgramV2(
                TriggerProgramV2.SCHEMA_VERSION,
                List.of(new TriggerProgramV2.EventBinding(
                        id(definition.id(), "event/0"), legacy.event().type(), legacy.event().configuration())),
                declarations, List.of(), body);
    }

    private static List<TriggerProgramV2.Statement> actions(
            UUID triggerId, List<TriggerDefinition.Action> actions, String path) {
        List<TriggerProgramV2.Statement> statements = new ArrayList<>();
        for (int index = 0; index < actions.size(); index++) {
            TriggerDefinition.Action action = actions.get(index);
            String nodePath = path + "/" + index;
            if (TriggerActionTree.CONDITION_TYPE.equals(action.type())) {
                TriggerDefinition.Condition condition = TriggerActionTree.condition(action);
                Map<String, Object> literal = Map.of(
                        "field", condition.field(), "operator", condition.operator(), "value", condition.value());
                TriggerExpression expression = new TriggerExpression(
                        id(triggerId, nodePath + "/expression"), TriggerExpression.Kind.FUNCTION,
                        "bool", null, "legacy_condition",
                        List.of(new TriggerExpression(
                                id(triggerId, nodePath + "/literal"), TriggerExpression.Kind.LITERAL,
                                "record", literal, "", List.of())));
                statements.add(new TriggerProgramV2.Statement(
                        id(triggerId, nodePath), TriggerProgramV2.StatementKind.IF, "", Map.of(),
                        expression, List.of(), actions(triggerId, action.children(), nodePath), List.of()));
                continue;
            }
            Map<String, TriggerExpression> inputs = new LinkedHashMap<>();
            action.parameters().forEach((name, value) -> inputs.put(name,
                    new TriggerExpression(id(triggerId, nodePath + "/" + name),
                            TriggerExpression.Kind.LITERAL, "string", value, "", List.of())));
            statements.add(new TriggerProgramV2.Statement(
                    id(triggerId, nodePath), TriggerProgramV2.StatementKind.ACTION,
                    action.type(), inputs, null, List.of(), List.of(), List.of()));
        }
        return List.copyOf(statements);
    }

    private static TriggerExpression combine(
            UUID triggerId, TriggerDefinition.MatchMode mode, List<TriggerExpression> values) {
        TriggerExpression result = values.get(0);
        for (int index = 1; index < values.size(); index++) {
            result = new TriggerExpression(
                    id(triggerId, "condition-combine/" + index), TriggerExpression.Kind.BINARY,
                    "bool", null, mode == TriggerDefinition.MatchMode.ALL ? "and" : "or",
                    List.of(result, values.get(index)));
        }
        return result;
    }

    private static UUID id(UUID triggerId, String path) {
        return UUID.nameUUIDFromBytes((triggerId + ":" + path).getBytes(StandardCharsets.UTF_8));
    }
}
