package com.xfestudio.xfeservermanager.core.content;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/** Declarative content only: no executable bytecode, arbitrary NBT, or host object references. */
public record ContentDefinition(String id, Kind kind, String name, String description,
                                String archetype, Map<String, String> properties, List<Behavior> behaviors) {
    public enum Kind { ITEM, BLOCK, ENTITY }
    public static final int MAX_BEHAVIORS = 32;

    public ContentDefinition {
        id = text(id, "content id", 1, 80);
        if (!id.matches("xfesmcontent:[a-z][a-z0-9_]{0,63}")) {
            throw new IllegalArgumentException("content id must be xfesmcontent:<lowercase_identifier>");
        }
        Objects.requireNonNull(kind, "content kind");
        name = text(name, "content name", 1, 96);
        description = text(description, "content description", 0, 2048);
        archetype = text(archetype, "content archetype", 1, 32);
        properties = safeMap(properties, "content properties", 48);
        behaviors = behaviors == null ? List.of() : List.copyOf(behaviors);
        if (behaviors.size() > MAX_BEHAVIORS) throw new IllegalArgumentException("too many content behaviors");
        var ids = new HashSet<String>();
        for (Behavior behavior : behaviors) {
            if (!ids.add(behavior.id())) throw new IllegalArgumentException("duplicate behavior id: " + behavior.id());
        }
        ContentCatalog.validate(kind, archetype, properties, behaviors);
    }

    public record Behavior(String id, String event, String action, Map<String, String> parameters,
                           int cooldownTicks, boolean cancelVanilla) {
        public Behavior {
            id = text(id, "behavior id", 1, 64);
            if (!id.matches("[A-Za-z_][A-Za-z0-9_-]{0,63}")) throw new IllegalArgumentException("invalid behavior id");
            event = text(event, "behavior event", 1, 32);
            action = text(action, "behavior action", 1, 32);
            parameters = safeMap(parameters, "behavior parameters", 16);
            if (cooldownTicks < 0 || cooldownTicks > 1_728_000) {
                throw new IllegalArgumentException("cooldownTicks must be between 0 and 1728000");
            }
        }
    }

    static Map<String, String> safeMap(Map<String, String> values, String name, int maximum) {
        if (values == null) return Map.of();
        if (values.size() > maximum) throw new IllegalArgumentException(name + " has too many entries");
        values.forEach((key, value) -> {
            text(key, name + " key", 1, 64);
            text(value, name + " value", 0, 1024);
        });
        return Map.copyOf(values);
    }

    static String text(String value, String name, int minimum, int maximum) {
        String safe = Objects.toString(value, "");
        if (safe.length() < minimum || safe.length() > maximum || safe.codePoints().anyMatch(Character::isISOControl)) {
            throw new IllegalArgumentException(name + " must contain " + minimum + "-" + maximum + " printable characters");
        }
        return safe;
    }
}
