package com.xfestudio.xfeservermanager.core.trigger;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;

/** Searchable, API-facing catalogue of variables that the trigger runtime really supplies. */
public final class TriggerVariableCatalog {
    private static final List<String> ALL = List.of("*");
    private static final List<String> PLAYER = List.of(
            "player.*", "block.break", "block.place", "block.tool_modify");
    private static final List<String> WORLD = ALL;
    public static final List<VariableDescriptor> VARIABLES = build();

    private TriggerVariableCatalog() { }

    /** Stable JSON shape consumed by the web variable picker. Keys omit the outer braces. */
    public record VariableDescriptor(
            String key,
            String nameZh,
            String nameEn,
            String descriptionZh,
            String descriptionEn,
            String category,
            List<String> scopes,
            String example,
            String formatHint,
            String sampleValue,
            String type,
            List<String> events,
            boolean templateAllowed,
            boolean conditionAllowed) {
        public VariableDescriptor {
            key = required(key, "key");
            nameZh = required(nameZh, "nameZh");
            nameEn = required(nameEn, "nameEn");
            descriptionZh = required(descriptionZh, "descriptionZh");
            descriptionEn = required(descriptionEn, "descriptionEn");
            category = required(category, "category");
            scopes = List.copyOf(Objects.requireNonNull(scopes, "scopes"));
            example = Objects.toString(example, "");
            formatHint = Objects.toString(formatHint, "");
            sampleValue = Objects.toString(sampleValue, "");
            type = required(type, "type");
            events = List.copyOf(Objects.requireNonNull(events, "events"));
            if (scopes.isEmpty() || events.isEmpty()) {
                throw new IllegalArgumentException("variable scopes and events must not be empty");
            }
        }

        private static String required(String value, String name) {
            value = Objects.requireNonNull(value, name).strip();
            if (value.isEmpty()) throw new IllegalArgumentException(name + " must not be blank");
            return value;
        }
    }

    private static List<VariableDescriptor> build() {
        List<VariableDescriptor> values = new ArrayList<>();

        // Compatibility aliases retained for messages migrated from the previous message system.
        add(values, "player", "玩家名（兼容）", "Player name (legacy)", "等同于 player.name。", "Alias of player.name.",
                "legacy", PLAYER, "{player}", "string");
        add(values, "online", "在线人数（兼容）", "Online players (legacy)", "等同于 server.online。", "Alias of server.online.",
                "legacy", ALL, "{online}", "number");
        add(values, "maxPlayers", "人数上限（兼容）", "Player limit (legacy)", "等同于 server.maxPlayers。", "Alias of server.maxPlayers.",
                "legacy", ALL, "{maxPlayers}", "number");
        add(values, "date", "事件日期（兼容）", "Event date (legacy)", "计划任务使用配置时区，其他事件使用服务器时区。", "Uses the configured schedule zone for schedules and the server zone otherwise.",
                "legacy", ALL, "{date}", "string");

        add(values, "event.type", "事件类型", "Event type", "当前触发事件的标识。", "Identifier of the current trigger event.",
                "event", ALL, "{event.type}", "string");
        add(values, "event.time", "事件捕获时间", "Event captured time", "事件进入系统时的 UTC ISO-8601 时间，不会被后台准备时间覆盖。", "UTC ISO-8601 instant captured when the event entered the system.",
                "event", ALL, "{event.time}", "instant");
        add(values, "trigger.preparedAt", "触发器准备时间", "Trigger prepared time", "后台完成条件准备时的 UTC ISO-8601 时间。", "UTC ISO-8601 instant when trigger matching was prepared.",
                "event", ALL, "{trigger.preparedAt}", "instant");

        String protectionEvents = "protection.*";
        event(values, "protection.kind", "防护类型", "Protection kind", "触发防护采样的负载类型。", "Load category that produced the protection sample.",
                protectionEvents, "item", "string");
        event(values, "protection.scope", "统计范围", "Protection scope", "dimension、chunk 或 global。", "One of dimension, chunk, or global.",
                protectionEvents, "dimension", "string");
        event(values, "protection.count", "当前数量/测量值", "Observed count/value", "本次采样到的实体数、速率、内存百分比或耗时毫秒。", "Entity count, rate, memory percentage, or elapsed milliseconds observed in this sample.",
                protectionEvents, "3447", "number");
        event(values, "protection.threshold", "服务端防护阈值", "Server protection threshold", "服务端硬限制所使用的阈值；触发器自己的阈值可能不同。", "Threshold used by the server circuit breaker; the trigger may use a different threshold.",
                protectionEvents, "2000", "number");
        event(values, "protection.excess", "超出数量", "Excess amount", "当前值超出服务端硬限制的非负数量。", "Non-negative amount above the server circuit-breaker limit.",
                protectionEvents, "1447", "number");
        event(values, "protection.removed", "已清理数量", "Removed count", "本次防护扫描自动清理的掉落物实体数。", "Dropped-item entities removed automatically by this scan.",
                protectionEvents, "1447", "number");
        event(values, "protection.action", "防护动作", "Protection action", "observed、blocked_spawn、blocked_command 或 removed_excess_items。", "One of observed, blocked_spawn, blocked_command, or removed_excess_items.",
                protectionEvents, "blocked_spawn", "string");
        event(values, "protection.reason", "拦截原因", "Protection reason", "自动拦截发生时的熔断原因。", "Circuit-breaker reason when an automatic block occurs.",
                protectionEvents, "entity spawn burst limit", "string");
        event(values, "protection.namespace", "模组命名空间", "Mod namespace", "实体注册 ID 的命名空间，例如 create 或 minecraft。", "Entity registry namespace, such as create or minecraft.",
                protectionEvents, "create", "string");
        event(values, "protection.windowTicks", "统计窗口游戏刻", "Window ticks", "生成速率统计窗口的游戏刻数。", "Game-tick length of the spawn-rate window.",
                protectionEvents, "100", "number");
        event(values, "protection.windowSeconds", "统计窗口秒数", "Window seconds", "速率统计窗口的秒数。", "Duration in seconds of the rate window.",
                protectionEvents, "5", "number");
        event(values, "protection.consecutive", "连续慢刻数", "Consecutive slow ticks", "服务端连续超过慢刻硬阈值的游戏刻数量。", "Consecutive ticks above the server slow-tick circuit threshold.",
                protectionEvents, "3", "number");
        event(values, "protection.heapUsedBytes", "堆内存已用字节", "Heap used bytes", "JVM 当前使用的堆内存。", "JVM heap memory currently in use.",
                "protection.memory_pressure", "8589934592", "number");
        event(values, "protection.heapMaxBytes", "堆内存上限字节", "Heap maximum bytes", "JVM 可用的最大堆内存。", "Maximum heap memory available to the JVM.",
                "protection.memory_pressure", "10737418240", "number");
        event(values, "protection.commandSource", "命令方块来源", "Command-block source", "被熔断的命令方块来源显示名称。", "Display name of the command-block source stopped by the circuit breaker.",
                "protection.command_block_rate", "@", "string");

        add(values, "server.time", "服务器本地时间", "Server local time", "事件捕获时的服务器本地时间。可在冒号后写格式。", "Server-local time captured with the event; accepts a format after a colon.",
                "time", ALL, "{server.time:uuuu-MM-dd HH:mm:ss}", TriggerTimeContext.FORMAT_HINT, "datetime");
        add(values, "server.time.iso", "带时区时间", "Offset date-time", "含 UTC 偏移量的 ISO-8601 服务器时间。", "ISO-8601 server time including its UTC offset.",
                "time", ALL, "{server.time.iso}", "string");
        add(values, "server.timezone", "服务器时区", "Server timezone", "服务器 JVM 的 IANA 时区标识。", "IANA timezone identifier used by the server JVM.",
                "time", ALL, "{server.timezone}", "string");
        add(values, "server.time.offset", "UTC 偏移", "UTC offset", "捕获时刻的服务器 UTC 偏移。", "Server UTC offset at the captured instant.",
                "time", ALL, "{server.time.offset}", "string");
        add(values, "server.time.epochSecond", "Unix 秒", "Unix seconds", "自 Unix 纪元起的秒数。", "Seconds since the Unix epoch.",
                "time", ALL, "{server.time.epochSecond}", "number");
        add(values, "server.time.epochMilli", "Unix 毫秒", "Unix milliseconds", "自 Unix 纪元起的毫秒数。", "Milliseconds since the Unix epoch.",
                "time", ALL, "{server.time.epochMilli}", "number");
        timePart(values, "year", "年", "Year", "四位公历年份", "four-digit calendar year");
        timePart(values, "month", "月", "Month", "月份数字（1-12）", "month number (1-12)");
        timePart(values, "monthName", "月份英文名", "Month name", "大写英文月份名", "uppercase English month name");
        timePart(values, "day", "日", "Day", "月内日期", "day of month");
        timePart(values, "dayOfYear", "年内日序", "Day of year", "一年中的第几天", "day number within the year");
        timePart(values, "dayOfWeek", "星期英文名", "Weekday name", "大写英文星期名", "uppercase English weekday name");
        timePart(values, "dayOfWeekNumber", "星期序号", "Weekday number", "ISO 星期序号（周一为 1）", "ISO weekday number (Monday is 1)");
        timePart(values, "weekOfYear", "ISO 周序号", "ISO week number", "ISO 周历中的周序号", "week number in the ISO week calendar");
        timePart(values, "hour24", "24 小时制小时", "24-hour clock", "0 到 23", "value from 0 to 23");
        timePart(values, "hour12", "12 小时制小时", "12-hour clock", "1 到 12", "value from 1 to 12");
        timePart(values, "minute", "分钟", "Minute", "0 到 59", "value from 0 to 59");
        timePart(values, "second", "秒", "Second", "0 到 59", "value from 0 to 59");
        timePart(values, "millisecond", "毫秒", "Millisecond", "0 到 999", "value from 0 to 999");
        timePart(values, "amPm", "上午/下午", "AM/PM", "AM 或 PM", "AM or PM");

        format(values, "uuuu-MM-dd HH:mm:ss", "完整日期时间（推荐）", "Full date and time (recommended)");
        format(values, "uuuu-MM-dd", "标准日期（推荐）", "ISO-like date (recommended)");
        format(values, "HH:mm:ss", "24 小时时间", "24-hour time");
        format(values, "uuuu/MM/dd HH:mm", "斜杠日期时间", "Slash date and time");
        format(values, "uuuu年MM月dd日 HH:mm:ss", "中文日期时间", "Chinese date and time");
        format(values, "MM-dd HH:mm", "月日与时间", "Month-day and time");
        format(values, "hh:mm:ss a", "12 小时时间", "12-hour time");
        format(values, "uuuu-MM-dd'T'HH:mm:ssXXX", "ISO 偏移时间", "ISO offset time");
        format(values, "EEEE HH:mm", "星期与时间", "Weekday and time");
        format(values, "uuuuMMdd-HHmmss", "紧凑时间戳", "Compact timestamp");
        format(values, "uuuu-MM-dd HH:mm:ss.SSS", "毫秒时间", "Time with milliseconds");
        format(values, "uuuu-MM-dd HH:mm:ss z", "带时区简称时间", "Time with zone name");
        format(values, "HH时mm分ss秒", "中文时分秒", "Chinese clock");
        format(values, "MM月dd日", "中文月日", "Chinese month and day");

        server(values, "defaultMessageSender", "默认消息发送者", "Default message sender", "消息未单独指定发送者时使用的名称。", "Name used when a message does not define its own sender.", "XFEServerManager", "string");
        server(values, "online", "在线玩家数", "Online players", "当前在线玩家数量。", "Current number of online players.", "5", "number");
        server(values, "maxPlayers", "玩家上限", "Maximum players", "服务器最大玩家数量。", "Configured maximum player count.", "20", "number");
        server(values, "availableSlots", "空余位置", "Available slots", "玩家上限减去在线人数。", "Maximum players minus online players.", "15", "number");
        server(values, "onlinePercent", "在线占用率", "Online percentage", "在线人数占上限的百分比。", "Percentage of player slots currently occupied.", "25.0", "number");
        server(values, "empty", "服务器无人", "Server empty", "没有在线玩家时为 true。", "True when no players are online.", "false", "boolean");
        server(values, "full", "服务器已满", "Server full", "在线人数达到上限时为 true。", "True when all player slots are occupied.", "false", "boolean");
        server(values, "uptimeSeconds", "运行秒数", "Uptime seconds", "本次服务器运行时长（秒）。", "Server uptime for this run, in seconds.", "3600", "number");
        server(values, "tps", "实际 TPS", "Observed TPS", "监控窗口内观测到的每秒刻数。", "Observed ticks per second in the metrics window.", "20.0", "number");
        server(values, "mspt.average", "平均 MSPT", "Average MSPT", "平均每刻耗时（毫秒）。", "Average milliseconds per tick.", "12.5", "number");
        server(values, "mspt.p95", "P95 MSPT", "P95 MSPT", "95% 分位每刻耗时。", "95th-percentile milliseconds per tick.", "18.0", "number");
        server(values, "mspt.p99", "P99 MSPT", "P99 MSPT", "99% 分位每刻耗时。", "99th-percentile milliseconds per tick.", "24.0", "number");
        server(values, "tickJitter", "刻间隔抖动", "Tick jitter", "刻间隔的标准差（毫秒）。", "Standard deviation of tick intervals in milliseconds.", "0.8", "number");
        server(values, "tickRate", "游戏速度", "Game tick rate", "服务器设置的目标游戏刻速率；正常为 20。", "Configured game tick rate; normally 20.", "20.0", "number");
        server(values, "tickPaused", "游戏暂停", "Game paused", "游戏刻被冻结时为 true。", "True when game ticking is frozen.", "false", "boolean");
        server(values, "tickCount", "服务器刻计数", "Server tick count", "服务器启动以来处理的刻数量。", "Ticks processed since server startup.", "72000", "number");
        server(values, "processCpuLoad", "进程 CPU 占用", "Process CPU load", "0 到 1 的 Java 进程 CPU 占用率。", "Java process CPU load from 0 to 1.", "0.21", "number");
        server(values, "heap.usedBytes", "堆已用字节", "Heap used bytes", "JVM 堆当前使用量。", "Current JVM heap usage in bytes.", "536870912", "number");
        server(values, "heap.committedBytes", "堆已提交字节", "Heap committed bytes", "JVM 已提交堆容量。", "Committed JVM heap capacity in bytes.", "1073741824", "number");
        server(values, "heap.maxBytes", "堆上限字节", "Maximum heap bytes", "JVM 堆容量上限。", "Maximum JVM heap capacity in bytes.", "4294967296", "number");
        server(values, "heap.freeBytes", "堆剩余字节", "Free heap bytes", "堆上限减去当前使用量。", "Maximum heap minus current usage.", "3758096384", "number");
        server(values, "heap.usedPercent", "堆占用率", "Heap usage percentage", "已用堆占上限的百分比。", "Percentage of maximum heap currently used.", "12.5", "number");
        server(values, "gc.count", "GC 次数", "GC count", "本次进程累计垃圾回收次数。", "Cumulative garbage-collection count for this process.", "42", "number");
        server(values, "gc.pauseMillis", "GC 暂停毫秒", "GC pause milliseconds", "本次进程累计垃圾回收耗时。", "Cumulative garbage-collection time for this process.", "320", "number");
        server(values, "diskFreeBytes", "磁盘可用字节", "Usable disk bytes", "服务器工作磁盘可用空间。", "Usable bytes on the server working disk.", "10737418240", "number");
        server(values, "averagePingMs", "平均延迟", "Average ping", "在线玩家的平均网络延迟（毫秒）。", "Average online-player latency in milliseconds.", "35.2", "number");
        server(values, "dimensionCount", "维度数量", "Dimension count", "当前已加载的维度数量。", "Number of currently loaded dimensions.", "3", "number");
        server(values, "loadedChunks", "已加载区块总数", "Loaded chunks", "所有维度已加载区块总数。", "Total loaded chunks across dimensions.", "625", "number");
        server(values, "entityCount", "实体总数", "Entity count", "所有维度实体总数。", "Total entities across dimensions.", "240", "number");
        server(values, "overworldDimension", "主世界维度 ID", "Overworld dimension ID", "服务器主世界的维度资源 ID。", "Resource identifier of the server overworld.", "minecraft:overworld", "string");
        add(values, "server.maintenanceEnabled", "维护模式已启用", "Maintenance enabled", "玩家加入时维护模式是否启用。", "Whether maintenance mode is enabled during a player join.",
                "server", List.of("player.join"), "{server.maintenanceEnabled}", "false", "", "boolean", true);
        add(values, "server.maintenanceMessage", "维护提示内容", "Maintenance message", "玩家加入时配置的维护提示内容。", "Configured maintenance notice during a player join.",
                "server", List.of("player.join"), "{server.maintenanceMessage}", "Server maintenance", "", "string", true);

        world(values, "dimension", "事件维度（兼容）", "Event dimension (legacy)", "事件提供维度时为事件维度，否则回退到主世界；新的配置建议使用 event.dimension 或 server.overworldDimension。", "Event dimension when supplied, otherwise the overworld; new configurations should prefer event.dimension or server.overworldDimension.", "minecraft:overworld", "string");
        world(values, "gameTime", "世界总游戏刻", "World game time", "世界创建以来累计的游戏刻。", "Total game ticks since world creation.", "1234567", "number");
        world(values, "dayTime", "世界昼夜时间", "World day time", "包含天数的昼夜刻计数。", "Day/night tick counter including elapsed days.", "96000", "number");
        world(values, "day", "游戏内天数", "In-game day", "从第 0 天开始的游戏内天数。", "In-game day number starting at zero.", "4", "number");
        world(values, "timeOfDay", "当日游戏刻", "Time-of-day tick", "当前游戏日内的刻数（0-23999）。", "Tick within the current Minecraft day (0-23999).", "6000", "number");
        world(values, "hour", "游戏内小时", "In-game hour", "Minecraft 时钟小时（0-23，刻 0 对应 06:00）。", "Minecraft clock hour (0-23; tick 0 is 06:00).", "12", "number");
        world(values, "minute", "游戏内分钟", "In-game minute", "Minecraft 时钟分钟。", "Minecraft clock minute.", "0", "number");
        world(values, "second", "游戏内秒", "In-game second", "Minecraft 时钟秒。", "Minecraft clock second.", "0", "number");
        world(values, "dayProgress", "游戏日进度", "Day progress", "当前游戏日已经过的百分比。", "Percentage elapsed in the current Minecraft day.", "25.0", "number");
        world(values, "isDay", "是否白天", "Is daytime", "主世界昼间时为 true。", "True during overworld daytime.", "true", "boolean");
        world(values, "isNight", "是否夜晚", "Is nighttime", "主世界夜间时为 true。", "True during overworld nighttime.", "false", "boolean");
        world(values, "timePeriod", "游戏时段", "Time period", "dawn、day、dusk 或 night。", "One of dawn, day, dusk, or night.", "day", "string");
        world(values, "realSeconds", "世界实际秒数", "World real seconds", "总游戏刻除以 20。", "Total game ticks divided by 20.", "61728", "number");
        world(values, "realMinutes", "世界实际分钟", "World real minutes", "总游戏刻折算的现实分钟。", "Total game ticks converted to real minutes.", "1028", "number");
        world(values, "tickOfHour", "游戏小时内刻", "Tick within hour", "当前 Minecraft 小时内的刻（0-999）。", "Tick within the current Minecraft hour (0-999).", "500", "number");
        world(values, "tickOfMinute", "游戏分钟内刻", "Tick within minute", "当前 Minecraft 分钟内经过的游戏刻（通常为 0-16）。", "Elapsed game ticks within the current Minecraft minute (normally 0-16).", "8", "number");
        world(values, "weather", "天气", "Weather", "clear、rain 或 thunder。", "One of clear, rain, or thunder.", "clear", "string");
        add(values, "weather.raining", "正在下雨", "Raining", "当前主世界有雨时为 true。", "True while it is raining in the overworld.",
                "world", WORLD, "{weather.raining}", "boolean");
        add(values, "weather.thundering", "正在雷暴", "Thundering", "当前主世界有雷暴时为 true。", "True during an overworld thunderstorm.",
                "world", WORLD, "{weather.thundering}", "boolean");
        add(values, "weather.clear", "天气晴朗", "Clear weather", "无雨且无雷暴时为 true。", "True when there is neither rain nor thunder.",
                "world", WORLD, "{weather.clear}", "boolean");
        world(values, "difficulty", "难度", "Difficulty", "当前主世界难度标识。", "Current overworld difficulty identifier.", "normal", "string");
        world(values, "loadedChunks", "维度已加载区块", "World loaded chunks", "主世界已加载区块数。", "Loaded chunk count in the overworld.", "300", "number");
        world(values, "entityCount", "维度实体数", "World entity count", "主世界快照中的实体数量。", "Entity count in the overworld snapshot.", "120", "number");
        world(values, "border.size", "世界边界大小", "World border size", "世界边界直径（方块）。", "World-border diameter in blocks.", "59999968", "number");
        world(values, "border.centerX", "边界中心 X", "Border center X", "世界边界中心 X 坐标。", "World-border center X coordinate.", "0", "number");
        world(values, "border.centerZ", "边界中心 Z", "Border center Z", "世界边界中心 Z 坐标。", "World-border center Z coordinate.", "0", "number");
        world(values, "border.warningBlocks", "边界警告距离", "Border warning distance", "世界边界警告距离（方块）。", "World-border warning distance in blocks.", "5", "number");
        world(values, "border.warningTime", "边界警告时间", "Border warning time", "世界边界警告时间（秒）。", "World-border warning time in seconds.", "15", "number");
        world(values, "spawn.x", "世界复活点 X", "World spawn X", "世界默认复活点的 X 坐标。", "X coordinate of the default world spawn.", "0", "integer");
        world(values, "spawn.y", "世界复活点 Y", "World spawn Y", "世界默认复活点的 Y 坐标。", "Y coordinate of the default world spawn.", "64", "integer");
        world(values, "spawn.z", "世界复活点 Z", "World spawn Z", "世界默认复活点的 Z 坐标。", "Z coordinate of the default world spawn.", "0", "integer");
        world(values, "spawn.position", "世界复活点坐标", "World spawn position", "可直接用于消息或指令的“X Y Z”坐标。", "Space-separated X Y Z world-spawn coordinates for messages or commands.", "0 64 0", "string");
        world(values, "spawn.dimension", "世界复活点维度", "World spawn dimension", "世界默认复活点所属维度。", "Dimension containing the default world spawn.", "minecraft:overworld", "string");
        world(values, "spawn.yaw", "世界复活点水平朝向", "World spawn yaw", "玩家在世界复活点生成时的水平朝向。", "Horizontal facing used at the default world spawn.", "0.0", "number");
        world(values, "spawn.pitch", "世界复活点俯仰角", "World spawn pitch", "玩家在世界复活点生成时的俯仰角；旧版本固定为 0。", "Pitch used at the default world spawn; zero on older versions.", "0.0", "number");
        world(values, "spawn.chunkX", "世界复活点区块 X", "World spawn chunk X", "世界复活点所在区块的 X 坐标。", "Chunk X containing the default world spawn.", "0", "integer");
        world(values, "spawn.chunkZ", "世界复活点区块 Z", "World spawn chunk Z", "世界复活点所在区块的 Z 坐标。", "Chunk Z containing the default world spawn.", "0", "integer");

        player(values, "uuid", "玩家 UUID", "Player UUID", "事件玩家的 UUID。", "UUID of the event player.", "00000000-0000-0000-0000-000000000000", "string");
        player(values, "name", "玩家名称", "Player name", "事件玩家当前名称。", "Current name of the event player.", "Steve", "string");
        player(values, "x", "玩家 X", "Player X", "事件捕获时的 X 坐标。", "X coordinate when the event was captured.", "12.5", "number");
        player(values, "y", "玩家 Y", "Player Y", "事件捕获时的 Y 坐标。", "Y coordinate when the event was captured.", "64", "number");
        player(values, "z", "玩家 Z", "Player Z", "事件捕获时的 Z 坐标。", "Z coordinate when the event was captured.", "-30.5", "number");
        player(values, "dimension", "玩家维度", "Player dimension", "事件捕获时玩家所在维度。", "Player dimension when the event was captured.", "minecraft:overworld", "string");
        player(values, "health", "玩家生命值", "Player health", "事件捕获时的生命值。", "Health at event capture.", "20.0", "number");
        player(values, "maxHealth", "玩家最大生命值", "Player maximum health", "玩家最大生命值。", "Player maximum health.", "20.0", "number");
        player(values, "healthPercent", "玩家生命百分比", "Player health percentage", "当前生命值占最大生命值的百分比。", "Current health as a percentage of maximum health.", "100.0", "number");
        player(values, "foodLevel", "饥饿值", "Food level", "玩家饥饿值。", "Player food level.", "20", "number");
        player(values, "saturation", "饱和度", "Saturation", "玩家食物饱和度。", "Player food saturation.", "5.0", "number");
        player(values, "experienceLevel", "经验等级", "Experience level", "玩家经验等级。", "Player experience level.", "30", "number");
        player(values, "totalExperience", "总经验", "Total experience", "玩家累计经验值。", "Player accumulated experience points.", "1395", "number");
        player(values, "experienceProgress", "本级经验进度", "Experience progress", "当前等级内经验进度（0-1）。", "Progress through the current experience level (0-1).", "0.5", "number");
        player(values, "gameMode", "游戏模式", "Game mode", "玩家当前游戏模式。", "Current player game mode.", "survival", "string");
        player(values, "creative", "创造模式", "Creative mode", "玩家处于创造模式时为 true。", "True when the player is in creative mode.", "false", "boolean");
        player(values, "spectator", "旁观模式", "Spectator mode", "玩家处于旁观模式时为 true。", "True when the player is spectating.", "false", "boolean");
        player(values, "alive", "存活", "Alive", "玩家尚未死亡时为 true。", "True while the player is alive.", "true", "boolean");
        player(values, "sleeping", "正在睡觉", "Sleeping", "玩家睡眠中时为 true。", "True while the player is sleeping.", "false", "boolean");
        player(values, "sprinting", "正在疾跑", "Sprinting", "玩家疾跑中时为 true。", "True while the player is sprinting.", "false", "boolean");
        player(values, "crouching", "正在潜行", "Crouching", "玩家潜行中时为 true。", "True while the player is crouching.", "false", "boolean");
        player(values, "swimming", "正在游泳", "Swimming", "玩家游泳中时为 true。", "True while the player is swimming.", "false", "boolean");
        player(values, "onFire", "正在燃烧", "On fire", "玩家燃烧中时为 true。", "True while the player is on fire.", "false", "boolean");
        player(values, "air", "剩余氧气", "Remaining air", "玩家剩余氧气刻数。", "Player remaining air supply in ticks.", "300", "number");
        player(values, "maxAir", "最大氧气", "Maximum air", "玩家最大氧气刻数。", "Player maximum air supply in ticks.", "300", "number");
        player(values, "armor", "护甲值", "Armor value", "玩家当前护甲点数。", "Current player armor points.", "20", "number");
        player(values, "yaw", "水平朝向", "Yaw", "玩家水平旋转角。", "Player horizontal rotation.", "90.0", "number");
        player(values, "pitch", "俯仰角", "Pitch", "玩家垂直旋转角。", "Player vertical rotation.", "0.0", "number");
        player(values, "invulnerable", "无敌状态", "Invulnerable", "玩家免疫伤害时为 true。", "True while the player is invulnerable.", "false", "boolean");
        player(values, "invisible", "隐身状态", "Invisible", "玩家不可见时为 true。", "True while the player is invisible.", "false", "boolean");
        player(values, "glowing", "发光状态", "Glowing", "玩家发光时为 true。", "True while the player is glowing.", "false", "boolean");
        player(values, "passenger", "正在乘坐", "Passenger", "玩家正乘坐实体时为 true。", "True while the player rides another entity.", "false", "boolean");
        player(values, "vehicle", "载有乘客", "Has passengers", "玩家实体载有乘客时为 true。", "True while the player entity has passengers.", "false", "boolean");
        player(values, "pingMs", "玩家延迟", "Player ping", "玩家网络延迟（毫秒）。", "Player network latency in milliseconds.", "42", "number");
        player(values, "respawn.exists", "已设置玩家复活点", "Player respawn is set", "玩家通过床或重生锚设置了个人复活点时为 true；为 false 时会使用世界复活点。", "True when the player has a personal bed or respawn-anchor spawn; otherwise the world spawn is used.", "true", "boolean");
        player(values, "respawn.x", "玩家复活点 X", "Player respawn X", "玩家个人复活点的 X 坐标；仅在 respawn.exists 为 true 时存在。", "Personal respawn X; available only when respawn.exists is true.", "120", "integer");
        player(values, "respawn.y", "玩家复活点 Y", "Player respawn Y", "玩家个人复活点的 Y 坐标；仅在 respawn.exists 为 true 时存在。", "Personal respawn Y; available only when respawn.exists is true.", "65", "integer");
        player(values, "respawn.z", "玩家复活点 Z", "Player respawn Z", "玩家个人复活点的 Z 坐标；仅在 respawn.exists 为 true 时存在。", "Personal respawn Z; available only when respawn.exists is true.", "-30", "integer");
        player(values, "respawn.position", "玩家复活点坐标", "Player respawn position", "可直接用于消息或指令的“X Y Z”个人复活点坐标。", "Space-separated personal respawn coordinates for messages or commands.", "120 65 -30", "string");
        player(values, "respawn.dimension", "玩家复活点维度", "Player respawn dimension", "玩家个人复活点所属维度。", "Dimension containing the player's personal respawn point.", "minecraft:overworld", "string");
        player(values, "respawn.yaw", "玩家复活点水平朝向", "Player respawn yaw", "玩家在个人复活点生成时的水平朝向。", "Horizontal facing used at the personal respawn point.", "90.0", "number");
        player(values, "respawn.pitch", "玩家复活点俯仰角", "Player respawn pitch", "玩家在个人复活点生成时的俯仰角；旧版本固定为 0。", "Pitch used at the personal respawn point; zero on older versions.", "0.0", "number");
        player(values, "respawn.forced", "强制使用玩家复活点", "Player respawn forced", "即使复活方块不可用也强制尝试该个人复活点时为 true。", "True when the personal respawn point is marked as forced.", "false", "boolean");
        player(values, "respawn.chunkX", "玩家复活点区块 X", "Player respawn chunk X", "玩家个人复活点所在区块的 X 坐标。", "Chunk X containing the personal respawn point.", "7", "integer");
        player(values, "respawn.chunkZ", "玩家复活点区块 Z", "Player respawn chunk Z", "玩家个人复活点所在区块的 Z 坐标。", "Chunk Z containing the personal respawn point.", "-2", "integer");
        add(values, "player.firstJoin", "首次加入", "First join", "迁移的首次加入消息流程中，首次加入时为 true。", "True for a first join when migrated join delivery state is enabled.",
                "player", List.of("player.join"), "{player.firstJoin}", "true", "", "boolean", true);
        add(values, "player.dailyDue", "每日消息待发送", "Daily message due", "迁移的每日消息本日尚未发送时为 true。", "True when a migrated daily message is still due for the player.",
                "player", List.of("player.join"), "{player.dailyDue}", "true", "", "boolean", true);
        add(values, "player.fromDimension", "来源维度", "Previous dimension", "玩家维度切换前的维度。", "Dimension before a player dimension change.",
                "player", List.of("player.dimension_change"), "{player.fromDimension}", "string");
        add(values, "player.toDimension", "目标维度", "New dimension", "玩家维度切换后的维度。", "Dimension after a player dimension change.",
                "player", List.of("player.dimension_change"), "{player.toDimension}", "string");

        event(values, "chat.message", "聊天内容", "Chat message", "玩家发送的原始聊天文本。", "Raw player chat text.", "player.chat", "Hello", "string");
        event(values, "command.value", "输入指令", "Entered command", "普通玩家指令事件中的完整指令文本。", "Complete command text in a normal player-command event.", "player.command", "/help", "string");
        event(values, "command.name", "触发指令名称", "Trigger command name", "自定义触发指令的根名称。", "Root name of the custom trigger command.", "player.command_trigger", "notice", "string");
        event(values, "command.raw", "触发指令原文", "Raw trigger command", "自定义触发指令的完整输入。", "Complete custom trigger command input.", "player.command_trigger", "notice Steve", "string");
        event(values, "args.<name>", "声明的指令参数", "Declared command argument", "将 <name> 替换为触发器声明的参数名，例如 args.target。", "Replace <name> with a declared trigger argument, such as args.target.", "player.command_trigger", "{args.target}", "string");
        event(values, "variable.name", "变化的变量名称", "Changed variable name", "variable.changed 事件中发生变化的变量名称。", "Name of the variable changed by a variable.changed event.", "variable.changed", "score", "string");
        event(values, "variable.type", "变化的变量类型", "Changed variable type", "发生变化变量的声明类型。", "Declared type of the changed variable.", "variable.changed", "integer", "string");
        event(values, "variable.visibility", "变化变量的可见范围", "Changed variable visibility", "trigger 或 global。", "Either trigger or global.", "variable.changed", "trigger", "string");
        event(values, "variable.storage", "变化变量的存储作用域", "Changed variable storage", "server、player、dimension 或 trigger。", "One of server, player, dimension, or trigger.", "variable.changed", "player", "string");
        event(values, "variable.storageKey", "变化变量的作用域键", "Changed variable storage key", "当前服务器、玩家 UUID、维度 ID 或触发器 ID。", "Current server, player UUID, dimension ID, or trigger ID.", "variable.changed", "00000000-0000-0000-0000-000000000000", "string");
        event(values, "variable.oldValue", "变量原值", "Previous variable value", "变量修改前的严格类型值。", "Strictly typed value before the change.", "variable.changed", "1", "string");
        event(values, "variable.newValue", "变量新值", "New variable value", "变量修改后的严格类型值。", "Strictly typed value after the change.", "variable.changed", "2", "string");
        event(values, "variable.sourceTriggerId", "变量来源触发器", "Variable source trigger", "修改变量的触发器 UUID。", "UUID of the trigger that changed the variable.", "variable.changed", "00000000-0000-0000-0000-000000000000", "uuid");
        event(values, "menu.id", "菜单 ID", "Menu ID", "当前打开或发生交互的菜单 UUID。", "UUID of the opened or interacted menu.",
                "menu.open/menu.close/menu.control", "00000000-0000-0000-0000-000000000000", "uuid");
        event(values, "menu.name", "菜单名称", "Menu name", "当前菜单经过变量解析后的名称。", "Rendered name of the current menu.",
                "menu.open/menu.close/menu.control", "主菜单", "string");
        event(values, "menu.sessionId", "菜单会话 ID", "Menu session ID", "用于校验玩家菜单交互的短期会话 UUID。", "Short-lived UUID used to validate a player's menu interaction.",
                "menu.open/menu.close/menu.control", "00000000-0000-0000-0000-000000000000", "uuid");
        event(values, "menu.controlId", "控件 ID", "Control ID", "menu.control 事件中的控件标识。", "Control identifier in a menu.control event.",
                "menu.control", "confirm", "string");
        event(values, "menu.controlType", "控件类型", "Control type", "产生交互的菜单控件类型。", "Type of the menu control that emitted the interaction.",
                "menu.control", "button", "string");
        event(values, "menu.event", "控件事件", "Control event", "点击、提交、切换或数值变化等事件名称。", "Click, submit, toggle, value-change, or another control event.",
                "menu.control", "click", "string");
        event(values, "menu.value", "控件值", "Control value", "输入框、滑块、复选框等控件提交的值。", "Value submitted by an input, slider, checkbox, or similar control.",
                "menu.control", "true", "string");
        add(values, "economy.primary.id", "主货币 ID", "Primary currency ID", "服务器主货币的 UUID。", "UUID of the server's primary currency.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.id}", "uuid");
        add(values, "economy.primary.code", "主货币代码", "Primary currency code", "服务器主货币的稳定代码。", "Stable code of the server's primary currency.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.code}", "string");
        add(values, "economy.primary.name", "主货币名称", "Primary currency name", "服务器主货币的显示名称。", "Display name of the server's primary currency.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.name}", "string");
        add(values, "economy.primary.symbol", "主货币符号", "Primary currency symbol", "服务器主货币的文本符号。", "Text symbol of the server's primary currency.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.symbol}", "string");
        add(values, "economy.primary.icon", "主货币图标", "Primary currency icon", "主货币使用的图片、物品或方块资源引用。", "Image, item, or block reference used by the primary currency.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.icon}", "string");
        add(values, "economy.primary.balance", "玩家主货币余额", "Primary balance", "当前事件玩家的主货币精确余额。", "Exact primary-currency balance of the event player.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.balance}", "number");
        add(values, "economy.primary.formatted", "格式化主货币余额", "Formatted primary balance", "主货币符号与玩家余额的组合文本。", "Primary symbol and player balance combined for display.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.primary.formatted}", "string");
        add(values, "economy.currencyCount", "货币数量", "Currency count", "服务器当前配置的货币总数（包括停用货币）。", "Total number of configured currencies, including disabled currencies.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.currencyCount}", "integer");
        add(values, "economy.balance.<currencyCode>", "指定货币余额", "Balance by currency code", "将 <currencyCode> 替换为货币代码，例如 economy.balance.coins。", "Replace <currencyCode> with a currency code, such as economy.balance.coins.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.balance.coins}", "number");
        add(values, "economy.formatted.<currencyCode>", "格式化指定货币余额", "Formatted balance by code", "将 <currencyCode> 替换为货币代码，结果包含货币符号。", "Replace <currencyCode> with a currency code; the value includes its symbol.",
                "economy", List.of("player.*", "block.*", "menu.*", "economy.*", "custom.*"), "{economy.formatted.coins}", "string");
        event(values, "economy.currency.id", "交易货币 ID", "Transaction currency ID", "经济事件所使用货币的 UUID。", "UUID of the currency used by an economy event.",
                "economy.*", "00000000-0000-0000-0000-000000000000", "uuid");
        event(values, "economy.currency.code", "交易货币代码", "Transaction currency code", "经济事件所使用货币的代码。", "Code of the currency used by an economy event.",
                "economy.*", "coins", "string");
        event(values, "economy.currency.name", "交易货币名称", "Transaction currency name", "经济事件所使用货币的名称。", "Name of the currency used by an economy event.",
                "economy.*", "金币", "string");
        event(values, "economy.currency.symbol", "交易货币符号", "Transaction currency symbol", "经济事件所使用货币的符号。", "Symbol of the currency used by an economy event.",
                "economy.*", "⛃", "string");
        event(values, "economy.currency.icon", "交易货币图标", "Transaction currency icon", "经济事件所使用货币的图片、物品、方块或贴图引用。", "Image, item, block, or texture reference of the currency used by an economy event.",
                "economy.*", "item:minecraft:emerald", "string");
        event(values, "economy.currency.fractionDigits", "交易货币小数位数", "Currency fraction digits", "经济事件货币允许的精确小数位数。", "Exact number of fraction digits allowed by the event currency.",
                "economy.*", "2", "integer");
        event(values, "economy.transaction.id", "交易 ID", "Transaction ID", "已提交经济流水的 UUID。", "UUID of the committed economy ledger entry.",
                "economy.*", "00000000-0000-0000-0000-000000000000", "uuid");
        event(values, "economy.transaction.type", "交易类型", "Transaction type", "deposit、withdraw、set 或 transfer。", "One of deposit, withdraw, set, or transfer.",
                "economy.*", "transfer", "string");
        event(values, "economy.transaction.amount", "交易金额", "Transaction amount", "当前经济流水的非负精确金额。", "Non-negative exact amount of the economy ledger entry.",
                "economy.*", "25.00", "number");
        event(values, "economy.transaction.reason", "交易原因", "Transaction reason", "经济操作填写的审计原因。", "Audit reason supplied for the economy operation.",
                "economy.*", "任务奖励", "string");
        event(values, "economy.transaction.origin", "交易来源", "Transaction origin", "经济操作来源，例如 web、command、trigger 或 system。", "Economy operation origin, such as web, command, trigger, or system.",
                "economy.*", "trigger", "string");
        event(values, "economy.transaction.actor", "交易执行者", "Transaction actor", "发起并写入该经济流水的账户、玩家或系统标识。", "Account, player, or system identity that committed the ledger entry.",
                "economy.*", "owner", "string");
        event(values, "economy.transaction.correlationId", "交易关联 ID", "Transaction correlation ID", "用于关联 Web 请求、指令或触发器执行的标识。", "Identifier correlating the transaction with a Web request, command, or trigger execution.",
                "economy.*", "request-id", "string");
        for (String side : List.of("source", "target")) {
            String zh = side.equals("source") ? "来源" : "目标";
            event(values, "economy." + side + ".uuid", zh + "玩家 UUID", side + " player UUID", "经济事件中的" + zh + "玩家 UUID。", "Player UUID on the " + side + " side of an economy event.",
                    "economy.*", "00000000-0000-0000-0000-000000000000", "uuid");
            event(values, "economy." + side + ".name", zh + "玩家名", side + " player name", "经济事件中的" + zh + "玩家名称。", "Player name on the " + side + " side of an economy event.",
                    "economy.*", "Steve", "string");
            event(values, "economy." + side + ".balanceBefore", zh + "变更前余额", side + " balance before", "经济操作前该侧玩家的余额。", "Balance on the " + side + " side before the operation.",
                    "economy.*", "100.00", "number");
            event(values, "economy." + side + ".balanceAfter", zh + "变更后余额", side + " balance after", "经济操作后该侧玩家的余额。", "Balance on the " + side + " side after the operation.",
                    "economy.*", "75.00", "number");
        }
        add(values, "trigger.id", "当前触发器 ID", "Current trigger ID", "当前执行触发器的 UUID。", "UUID of the trigger currently executing.", "trigger", List.of("global"), "{trigger.id}", "uuid");
        add(values, "trigger.name", "当前触发器名称", "Current trigger name", "当前执行触发器的名称。", "Name of the trigger currently executing.", "trigger", List.of("global"), "{trigger.name}", "string");
        add(values, "trigger.executionId", "执行实例 ID", "Execution ID", "本次可恢复执行的唯一 UUID。", "Unique UUID of this resumable execution.", "trigger", List.of("global"), "{trigger.executionId}", "uuid");
        add(values, "trigger.callerId", "调用方触发器 ID", "Caller trigger ID", "由另一个触发器调用时的来源 UUID。", "Source UUID when invoked by another trigger.", "trigger", List.of("global"), "{trigger.callerId}", "uuid");
        event(values, "advancement.id", "进度 ID", "Advancement ID", "玩家获得的进度资源 ID。", "Resource identifier of the awarded advancement.", "player.advancement", "minecraft:story/mine_stone", "string");
        event(values, "damage.amount", "伤害量", "Damage amount", "玩家受伤事件的伤害量。", "Damage amount in a player-hurt event.", "player.hurt", "4.0", "number");
        event(values, "damage.type", "伤害类型", "Damage type", "玩家受伤事件的伤害类型标识。", "Damage-type identifier in a player-hurt event.", "player.hurt", "mob", "string");
        event(values, "heal.amount", "治疗量", "Heal amount", "玩家治疗事件的恢复量。", "Restored health in a player-heal event.", "player.heal", "2.0", "number");
        for (String part : List.of("uuid", "type", "name", "x", "y", "z")) {
            String type = Set.of("x", "y", "z").contains(part) ? "number" : "string";
            event(values, "attacker." + part, "攻击者" + partZh(part), "Attacker " + part,
                    part.equals("name")
                            ? "攻击者的友好显示名：玩家名和命名生物保持原名，未命名生物随接收玩家的客户端语言本地化；稳定判断请使用 attacker.type。"
                            : "造成伤害的实体信息；没有直接攻击者时不存在。",
                    part.equals("name")
                            ? "Friendly attacker name: player/custom names remain literal and unnamed entities localize in each recipient's client; use attacker.type for stable matching."
                            : "Damaging-entity data; absent when there is no direct attacker.",
                    "player.hurt", part.equals("type") ? "minecraft:drowned" : part.equals("name") ? "溺尸" : "", type);
        }
        event(values, "attacker.scoreboardName", "攻击者计分板名称", "Attacker scoreboard name", "攻击者的原始计分板名称；未命名生物可能是 UUID，通常应显示 attacker.name。", "Raw scoreboard name of the attacker; unnamed entities may expose a UUID, so attacker.name is preferred for display.",
                "player.hurt", "545696f6-8dab-4803-b681-08eee55109a8", "string");
        event(values, "attacker.customName", "攻击者是否已命名", "Attacker has custom name", "攻击者是使用命名牌等方式命名的生物时为 true。", "True when the attacking entity has a custom name.",
                "player.hurt", "false", "boolean");
        event(values, "attacker.nameTranslationKey", "攻击者名称翻译键", "Attacker name translation key", "未命名非玩家实体的客户端翻译键；玩家或已命名生物中不存在。", "Client translation key for an unnamed non-player entity; absent for players and custom-named entities.",
                "player.hurt", "entity.minecraft.drowned", "string");
        for (String part : List.of("uuid", "type", "name", "x", "y", "z")) {
            String type = Set.of("x", "y", "z").contains(part) ? "number" : "string";
            List<String> entityEvents = part.equals("uuid")
                    ? List.of("entity.*", "player.attack", "player.entity_interact", "player.item_pickup", "player.item_drop")
                    : part.equals("name") ? List.of("player.attack")
                    : List.of("entity.*", "player.attack", "player.entity_interact");
            add(values, "entity." + part, "实体" + partZh(part), "Entity " + part,
                    part.equals("name")
                            ? "目标实体的友好显示名；玩家和命名生物保持原名，未命名生物在消息中按客户端语言本地化。"
                            : "实体相关事件中的目标实体信息。",
                    part.equals("name")
                            ? "Friendly target name; player/custom names remain literal and unnamed entities localize in messages."
                            : "Target entity data in entity-related events.", "entity",
                    entityEvents,
                    "{entity." + part + "}", type);
        }
        event(values, "attack.hand", "攻击使用手", "Attack hand", "玩家攻击事件使用的手；普通攻击为 main_hand。", "Hand used by the player attack; normal attacks use main_hand.",
                "player.attack", "main_hand", "string");
        event(values, "attack.pvp", "是否攻击玩家", "PVP attack", "攻击目标是玩家时为 true。", "True when the attack target is a player.",
                "player.attack", "false", "boolean");
        event(values, "attack.strength", "攻击冷却完成度", "Attack strength", "攻击发生前的冷却完成度，范围通常为 0 到 1；该事件发生在最终伤害结算前。", "Attack cooldown readiness before final damage resolution, normally from 0 to 1.",
                "player.attack", "1.0", "number");
        event(values, "attack.targetDistance", "攻击目标距离", "Target distance", "攻击玩家与目标实体之间的直线距离（方块）。", "Straight-line distance in blocks between the attacking player and target.",
                "player.attack", "2.75", "number");
        event(values, "attack.hasLineOfSight", "攻击目标可见", "Target in line of sight", "攻击玩家对目标实体具有直接视线时为 true。", "True when the attacking player has line of sight to the target.",
                "player.attack", "true", "boolean");
        for (String part : List.of("uuid", "entityId", "type", "name", "scoreboardName", "customName", "nameTranslationKey",
                "dimension", "x", "y", "z", "yaw", "pitch",
                "velocityX", "velocityY", "velocityZ", "width", "height", "alive", "living", "player", "onFire",
                "invulnerable", "invisible", "glowing", "passenger", "vehicle", "health", "maxHealth",
                "healthPercent", "absorption", "armor")) {
            String type = switch (part) {
                case "alive", "living", "player", "customName", "onFire", "invulnerable", "invisible", "glowing", "passenger", "vehicle" -> "boolean";
                case "uuid" -> "uuid";
                case "type", "name", "scoreboardName", "nameTranslationKey", "dimension" -> "string";
                case "entityId", "armor" -> "integer";
                default -> "number";
            };
            String sample = switch (part) {
                case "uuid" -> "00000000-0000-0000-0000-000000000000";
                case "entityId" -> "42";
                case "type" -> "minecraft:zombie";
                case "name" -> "Zombie";
                case "scoreboardName" -> "Zombie";
                case "nameTranslationKey" -> "entity.minecraft.zombie";
                case "dimension" -> "minecraft:overworld";
                case "alive", "living" -> "true";
                case "player", "customName", "onFire", "invulnerable", "invisible", "glowing", "passenger", "vehicle" -> "false";
                case "health", "maxHealth" -> "20.0";
                case "healthPercent" -> "100.0";
                case "armor" -> "2";
                default -> "0.0";
            };
            event(values, "attack.target." + part, "攻击目标" + attackPartZh(part), "Attack target " + part,
                    attackTargetDescriptionZh(part), attackTargetDescriptionEn(part), "player.attack", sample, type);
        }
        event(values, "attack.weapon.empty", "攻击主手为空", "Empty attack hand", "玩家主手没有物品时为 true。", "True when the player's main hand is empty.",
                "player.attack", "false", "boolean");
        event(values, "attack.weapon.id", "攻击武器 ID", "Attack weapon ID", "玩家攻击时主手物品的资源 ID；空手时为 minecraft:air。", "Resource identifier of the main-hand item; minecraft:air for an empty hand.",
                "player.attack", "minecraft:diamond_sword", "string");
        event(values, "attack.weapon.name", "攻击武器名称", "Attack weapon name", "玩家攻击时主手物品的显示名称。", "Display name of the player's main-hand item.",
                "player.attack", "Diamond Sword", "string");
        event(values, "attack.weapon.count", "攻击武器数量", "Attack weapon count", "玩家攻击时主手物品堆叠数量。", "Stack count of the player's main-hand item.",
                "player.attack", "1", "integer");
        event(values, "attack.weapon.damageable", "攻击武器可损耗", "Damageable attack weapon", "主手物品具有耐久度时为 true。", "True when the main-hand item has durability.",
                "player.attack", "true", "boolean");
        event(values, "attack.weapon.damage", "攻击武器已损耗耐久", "Attack weapon damage", "主手物品已经损耗的耐久值。", "Durability damage already taken by the main-hand item.",
                "player.attack", "12", "integer");
        event(values, "attack.weapon.maxDamage", "攻击武器最大耐久", "Attack weapon maximum durability", "主手物品的最大耐久值；不可损耗物品为 0。", "Maximum durability of the main-hand item; zero for non-damageable items.",
                "player.attack", "1561", "integer");
        event(values, "attack.weapon.remainingDurability", "攻击武器剩余耐久", "Attack weapon remaining durability", "主手物品在攻击事件发生时的剩余耐久。", "Remaining durability of the main-hand item when the attack event fires.",
                "player.attack", "1549", "integer");
        event(values, "item.id", "物品 ID", "Item ID", "事件物品的资源 ID。", "Resource identifier of the event item.",
                "player.item_pickup/player.item_drop/player.item_use/player.item_use_finish/player.item_craft/player.interact/player.entity_interact/block.tool_modify",
                "minecraft:diamond", "string");
        event(values, "item.count", "物品数量", "Item count", "事件物品堆叠数量。", "Stack size of the event item.",
                "player.item_pickup/player.item_drop/player.item_use/player.item_use_finish/player.item_craft/player.interact/player.entity_interact/block.tool_modify",
                "3", "number");
        event(values, "item.resultId", "使用结果物品 ID", "Use result item ID", "物品使用结束后的结果物品。", "Result item after finishing item use.",
                "player.item_use_finish", "minecraft:glass_bottle", "string");
        event(values, "item.resultCount", "使用结果数量", "Use result count", "物品使用结束后的结果数量。", "Result stack size after finishing item use.",
                "player.item_use_finish", "1", "number");
        event(values, "use.duration", "使用持续刻数", "Use duration", "完成物品使用所持续的游戏刻数。", "Game ticks spent completing item use.",
                "player.item_use_finish", "32", "number");
        event(values, "interaction.hand", "交互手", "Interaction hand", "main_hand 或 off_hand。", "Either main_hand or off_hand.",
                "player.interact/player.entity_interact/player.item_use", "main_hand", "string");
        event(values, "block.id", "方块 ID", "Block ID", "事件方块的资源 ID。", "Resource identifier of the event block.",
                "block.*/player.interact", "minecraft:stone", "string");
        event(values, "block.previousId", "原方块 ID", "Previous block ID", "变化前方块的资源 ID。", "Resource identifier before a block change.",
                "block.change/block.grow/block.tool_modify", "minecraft:dirt", "string");
        for (String axis : List.of("x", "y", "z")) {
            event(values, "block." + axis, "方块 " + axis.toUpperCase(java.util.Locale.ROOT),
                    "Block " + axis.toUpperCase(java.util.Locale.ROOT), "事件方块坐标。", "Event block coordinate.",
                    "block.*/player.interact", "0", "number");
        }
        event(values, "tool.action", "工具动作", "Tool action", "导致方块变化的 Forge 工具动作名。", "Forge tool-action name that changed the block.",
                "block.tool_modify", "axe_strip", "string");
        for (String axis : List.of("x", "y", "z")) {
            event(values, "sleep." + axis, "睡眠位置 " + axis.toUpperCase(java.util.Locale.ROOT),
                    "Sleep position " + axis.toUpperCase(java.util.Locale.ROOT), "玩家尝试睡眠的床坐标。", "Bed coordinate for the sleep attempt.",
                    "player.sleep", "0", "number");
        }
        event(values, "sleep.result", "睡眠结果", "Sleep result", "睡眠尝试结果，成功时为 success。", "Sleep-attempt result; success when accepted.",
                "player.sleep", "success", "string");
        event(values, "chunk.x", "区块 X", "Chunk X", "区块事件的 X 坐标。", "Chunk X coordinate.", "chunk.*", "2", "number");
        event(values, "chunk.z", "区块 Z", "Chunk Z", "区块事件的 Z 坐标。", "Chunk Z coordinate.", "chunk.*", "-3", "number");
        event(values, "chunk.new", "新区块", "New chunk", "首次生成区块时为 true。", "True when a loaded chunk is newly generated.", "chunk.load", "true", "boolean");
        event(values, "explosion.affectedBlocks", "爆炸影响方块数", "Explosion affected blocks", "爆炸影响的方块数量。", "Number of blocks affected by an explosion.",
                "world.explosion", "16", "number");
        event(values, "event.dimension", "事件发生维度", "Event dimension", "方块、实体、世界或区块事件实际发生的维度；不适用的事件中不存在。", "Dimension in which a block, entity, world, or chunk event occurred; absent for unrelated events.",
                "block.*/entity.*/world.*/chunk.*/player.interact/player.attack/player.entity_interact", "minecraft:overworld", "string");

        add(values, "schedule.occurrence", "计划发生标识", "Schedule occurrence", "本次被持久化认领的计划发生标识。", "Durably claimed identifier for this schedule occurrence.",
                "schedule", List.of("schedule.daily", "schedule.interval"), "{schedule.occurrence}", "string");
        add(values, "schedule.scheduledAt", "计划执行时刻", "Scheduled instant", "本次计划原本应执行的 UTC ISO-8601 时刻。", "UTC ISO-8601 instant at which this occurrence was due.",
                "schedule", List.of("schedule.daily", "schedule.interval"), "{schedule.scheduledAt}", "instant");
        add(values, "schedule.lateBySeconds", "计划延迟秒数", "Schedule lateness", "从应执行时刻到本次准备时刻的非负秒数。", "Non-negative seconds between due time and preparation.",
                "schedule", List.of("schedule.daily", "schedule.interval"), "{schedule.lateBySeconds}", "number");
        add(values, "schedule.timezone", "计划时区", "Schedule timezone", "每日计划配置的时区；固定间隔使用服务器时区。", "Configured zone for daily schedules; server zone for intervals.",
                "schedule", List.of("schedule.daily", "schedule.interval"), "{schedule.timezone}", "string");
        add(values, "schedule.intervalSeconds", "间隔总秒数", "Interval seconds", "固定间隔触发器配置的总秒数。", "Configured total seconds for an interval trigger.",
                "schedule", List.of("schedule.interval"), "{schedule.intervalSeconds}", "number");

        Set<String> unique = new HashSet<>();
        for (VariableDescriptor value : values) {
            if (!unique.add(value.key())) throw new IllegalStateException("duplicate trigger variable " + value.key());
        }
        if (values.size() < 100) throw new IllegalStateException("trigger variable catalogue must contain at least 100 entries");
        return List.copyOf(values);
    }

    private static void timePart(List<VariableDescriptor> values, String suffix, String zh, String en,
                                 String detailZh, String detailEn) {
        add(values, "server.time." + suffix, zh, en, "服务器本地时间的" + detailZh + "。",
                "The " + detailEn + " of server-local time.", "time", ALL,
                "{server.time." + suffix + "}", suffix.matches(".*Name|amPm") ? "string" : "number");
    }

    private static void format(List<VariableDescriptor> values, String pattern, String zh, String en) {
        add(values, "server.time:" + pattern, zh, en, "服务器本地时间格式预设：" + pattern + "。",
                "Server-local time format preset: " + pattern + ".", "time", ALL,
                "{server.time:" + pattern + "}", "", TriggerTimeContext.FORMAT_HINT, "string", false);
    }

    private static void server(List<VariableDescriptor> values, String suffix, String zh, String en,
                               String descriptionZh, String descriptionEn, String sample, String type) {
        add(values, "server." + suffix, zh, en, descriptionZh, descriptionEn,
                "server", ALL, "{server." + suffix + "}", sample, "", type, true);
    }

    private static void world(List<VariableDescriptor> values, String suffix, String zh, String en,
                              String descriptionZh, String descriptionEn, String sample, String type) {
        if (!suffix.equals("dimension")) {
            descriptionZh += "（来自服务器主世界状态快照。）";
            descriptionEn += " (From the server overworld status snapshot.)";
        }
        add(values, "world." + suffix, zh, en, descriptionZh, descriptionEn,
                "world", WORLD, "{world." + suffix + "}", sample, "", type, true);
    }

    private static void player(List<VariableDescriptor> values, String suffix, String zh, String en,
                               String descriptionZh, String descriptionEn, String sample, String type) {
        add(values, "player." + suffix, zh, en, descriptionZh, descriptionEn,
                "player", PLAYER, "{player." + suffix + "}", sample, "", type, true);
    }

    private static void event(List<VariableDescriptor> values, String key, String zh, String en,
                              String descriptionZh, String descriptionEn, String event,
                              String sample, String type) {
        add(values, key, zh, en, descriptionZh, descriptionEn,
                "event", List.of(event.split("/")), "{" + key + "}", sample, "", type, true);
    }

    private static void add(List<VariableDescriptor> values, String key, String zh, String en,
                            String descriptionZh, String descriptionEn, String category,
                            List<String> scopes, String example, String type) {
        add(values, key, zh, en, descriptionZh, descriptionEn, category, scopes,
                example, "", "", type, true);
    }

    private static void add(List<VariableDescriptor> values, String key, String zh, String en,
                            String descriptionZh, String descriptionEn, String category,
                            List<String> scopes, String example, String formatHint, String type) {
        add(values, key, zh, en, descriptionZh, descriptionEn, category, scopes,
                example, "", formatHint, type, true);
    }

    private static void add(List<VariableDescriptor> values, String key, String zh, String en,
                            String descriptionZh, String descriptionEn, String category,
                            List<String> events, String example, String sampleValue,
                            String formatHint, String type, boolean conditionAllowed) {
        List<String> scopes = semanticScopes(category, key, events);
        values.add(new VariableDescriptor(key, zh, en, descriptionZh, descriptionEn, category,
                scopes, example, formatHint, sampleValue, type, events, true,
                conditionAllowed && (!category.equals("legacy") || key.equals("date"))));
    }

    private static List<String> semanticScopes(String category, String key, List<String> events) {
        return switch (category) {
            case "time" -> List.of("global");
            case "server" -> events.equals(ALL) ? List.of("global") : List.of("event-specific");
            case "world" -> List.of("world");
            case "player" -> List.of("player-event");
            case "schedule" -> List.of("schedule");
            case "event", "entity" -> key.startsWith("command.") || key.startsWith("args.")
                    ? List.of("command") : List.of("event-specific");
            case "legacy" -> events.equals(ALL) ? List.of("global") : List.of("player-event");
            default -> List.of("event-specific");
        };
    }

    private static String partZh(String part) {
        return switch (part) {
            case "uuid" -> " UUID";
            case "type" -> "类型";
            case "name" -> "名称";
            case "scoreboardName" -> "计分板名称";
            case "customName" -> "是否已命名";
            case "nameTranslationKey" -> "名称翻译键";
            case "x", "y", "z" -> " " + part.toUpperCase(java.util.Locale.ROOT);
            default -> part;
        };
    }

    private static String attackPartZh(String part) {
        return switch (part) {
            case "uuid" -> " UUID";
            case "entityId" -> "运行时实体 ID";
            case "type" -> "类型";
            case "name" -> "名称";
            case "scoreboardName" -> "计分板名称";
            case "customName" -> "是否已命名";
            case "nameTranslationKey" -> "名称翻译键";
            case "dimension" -> "所在维度";
            case "x", "y", "z" -> "坐标 " + part.toUpperCase(java.util.Locale.ROOT);
            case "yaw" -> "水平朝向";
            case "pitch" -> "俯仰角";
            case "velocityX", "velocityY", "velocityZ" -> "速度 " + part.substring(part.length() - 1);
            case "width" -> "碰撞箱宽度";
            case "height" -> "碰撞箱高度";
            case "alive" -> "存活状态";
            case "living" -> "是否为生物";
            case "player" -> "是否为玩家";
            case "onFire" -> "着火状态";
            case "invulnerable" -> "无敌状态";
            case "invisible" -> "隐身状态";
            case "glowing" -> "发光状态";
            case "passenger" -> "乘客状态";
            case "vehicle" -> "载具状态";
            case "health" -> "当前生命值";
            case "maxHealth" -> "最大生命值";
            case "healthPercent" -> "生命百分比";
            case "absorption" -> "伤害吸收值";
            case "armor" -> "护甲值";
            default -> part;
        };
    }

    private static String attackTargetDescriptionZh(String part) {
        return switch (part) {
            case "uuid" -> "被攻击实体的永久 UUID。";
            case "entityId" -> "被攻击实体在当前服务器会话内的临时数字 ID。";
            case "type" -> "被攻击实体的注册资源 ID。";
            case "name" -> "被攻击实体的友好显示名：玩家名和命名生物保持原名，未命名生物随接收玩家的客户端语言本地化；稳定判断请使用 attack.target.type。";
            case "scoreboardName" -> "被攻击实体的原始计分板名称；未命名生物可能是 UUID。";
            case "customName" -> "被攻击实体使用命名牌等方式命名时为 true。";
            case "nameTranslationKey" -> "未命名非玩家目标的客户端翻译键；玩家或已命名生物中不存在。";
            case "dimension" -> "攻击发生时目标实体所在的维度资源 ID。";
            case "x", "y", "z" -> "攻击发生时目标实体的精确 " + part.toUpperCase(java.util.Locale.ROOT) + " 坐标。";
            case "yaw" -> "攻击发生时目标实体的水平朝向角度。";
            case "pitch" -> "攻击发生时目标实体的俯仰角度。";
            case "velocityX", "velocityY", "velocityZ" -> "攻击发生前目标实体在该轴上的移动速度。";
            case "width" -> "目标实体碰撞箱的宽度（方块）。";
            case "height" -> "目标实体碰撞箱的高度（方块）。";
            case "alive" -> "攻击事件发生时目标实体仍存活则为 true。";
            case "living" -> "目标属于生物实体（具有生命和护甲属性）时为 true。";
            case "player" -> "目标是服务器玩家时为 true。";
            case "onFire" -> "目标实体正在着火时为 true。";
            case "invulnerable" -> "目标实体处于无敌状态时为 true。";
            case "invisible" -> "目标实体处于隐身状态时为 true。";
            case "glowing" -> "目标实体具有发光轮廓时为 true。";
            case "passenger" -> "目标实体正骑乘其他实体时为 true。";
            case "vehicle" -> "目标实体正搭载其他实体时为 true。";
            case "health" -> "伤害结算前目标生物的当前生命值；非生物目标不存在。";
            case "maxHealth" -> "目标生物的最大生命值；非生物目标不存在。";
            case "healthPercent" -> "伤害结算前目标生物的生命百分比；非生物目标不存在。";
            case "absorption" -> "伤害结算前目标生物的伤害吸收值；非生物目标不存在。";
            case "armor" -> "目标生物的当前护甲值；非生物目标不存在。";
            default -> "玩家攻击事件中的目标实体快照。";
        };
    }

    private static String attackTargetDescriptionEn(String part) {
        return switch (part) {
            case "uuid" -> "Persistent UUID of the attacked entity.";
            case "entityId" -> "Temporary numeric ID of the attacked entity in the current server session.";
            case "type" -> "Registered resource identifier of the attacked entity type.";
            case "name" -> "Friendly target name: player/custom names remain literal and unnamed entities localize in each recipient's client; use attack.target.type for stable matching.";
            case "scoreboardName" -> "Raw scoreboard name of the target; unnamed entities may expose a UUID.";
            case "customName" -> "True when the attacked entity has a custom name.";
            case "nameTranslationKey" -> "Client translation key for an unnamed non-player target; absent for players and custom-named entities.";
            case "dimension" -> "Dimension resource identifier containing the target when the attack occurs.";
            case "x", "y", "z" -> "Exact target " + part.toUpperCase(java.util.Locale.ROOT) + " coordinate when the attack occurs.";
            case "yaw" -> "Horizontal rotation of the target when the attack occurs.";
            case "pitch" -> "Vertical rotation of the target when the attack occurs.";
            case "velocityX", "velocityY", "velocityZ" -> "Target movement velocity on this axis before damage resolution.";
            case "width" -> "Width of the target bounding box in blocks.";
            case "height" -> "Height of the target bounding box in blocks.";
            case "alive" -> "True when the target is alive as the attack event fires.";
            case "living" -> "True for a living target with health and armor attributes.";
            case "player" -> "True when the target is a server player.";
            case "onFire" -> "True when the target is on fire.";
            case "invulnerable" -> "True when the target is invulnerable.";
            case "invisible" -> "True when the target is invisible.";
            case "glowing" -> "True when the target has a glowing outline.";
            case "passenger" -> "True when the target is riding another entity.";
            case "vehicle" -> "True when another entity is riding the target.";
            case "health" -> "Target living-entity health before damage resolution; absent for non-living targets.";
            case "maxHealth" -> "Maximum target health; absent for non-living targets.";
            case "healthPercent" -> "Target health percentage before damage resolution; absent for non-living targets.";
            case "absorption" -> "Target absorption amount before damage resolution; absent for non-living targets.";
            case "armor" -> "Current target armor value; absent for non-living targets.";
            default -> "Target-entity snapshot for a player attack event.";
        };
    }
}
