// Tab-completion + suggestion dropdown for the console command input.
//   - Ghost text shows the top match inline (Tab to accept)
//   - A dropdown list shows up to N matches with their argument signature
//     (MC-style: <required> [optional] <a|b|c> for choices)
//   - Click a list item to insert it; arrow keys + Enter also navigate the list
//     when it's open
"use strict";

import { state } from "./state.js";
import { escapeHtml } from "./api.js";

const COMMANDS = [
    "help", "list", "say", "tell", "msg", "me", "w",
    "op", "deop",
    "whitelist", "ban", "ban-ip", "pardon", "pardon-ip", "banlist",
    "kick",
    "tp", "teleport",
    "give", "clear", "kill",
    "gamemode", "gamerule", "difficulty",
    "weather", "time", "seed", "spawnpoint", "setworldspawn",
    "save-all", "save-on", "save-off", "stop", "reload",
    "xp", "experience", "effect", "enchant",
    "summon", "data", "execute", "fill", "setblock", "locate", "tag",
    "ftbchunks", "ftbteams",
    "chunky",
    "kubejs", "kjs",
];

const SUBCOMMANDS = {
    whitelist:  ["add", "remove", "list", "reload", "on", "off"],
    gamemode:   ["survival", "creative", "adventure", "spectator"],
    weather:    ["clear", "rain", "thunder"],
    difficulty: ["peaceful", "easy", "normal", "hard"],
    time:       ["set", "add", "query"],
    chunky:     ["start", "cancel", "pause", "continue", "world", "shape", "center", "radius", "force_load", "force_unload", "trim", "help"],
    ftbchunks:  ["claim", "unclaim", "load", "unload", "admin"],
};

const TAKES_PLAYER_AT = {
    "op": 1, "deop": 1, "tp": 1, "teleport": 1, "kick": 1,
    "ban": 1, "pardon": 1, "kill": 1,
    "tell": 1, "msg": 1, "w": 1,
    "give": 1, "clear": 1, "effect": 1, "enchant": 1, "xp": 1, "experience": 1,
    "whitelist add": 2, "whitelist remove": 2,
    "gamemode survival": 2, "gamemode creative": 2, "gamemode adventure": 2, "gamemode spectator": 2,
};

// MC-style argument signatures for each command. Shown as a hint after the name
// in the suggestion list. <required> [optional] <a|b|c> for enum choices.
const SIGNATURES = {
    help:           "[command]",
    list:           "",
    say:            "<message>",
    tell:           "<player> <message>",
    msg:            "<player> <message>",
    me:             "<action>",
    w:              "<player> <message>",
    op:             "<player>",
    deop:           "<player>",
    whitelist:      "<add|remove|list|reload|on|off>",
    "whitelist add":     "<player>",
    "whitelist remove":  "<player>",
    "whitelist list":    "",
    "whitelist on":      "",
    "whitelist off":     "",
    "whitelist reload":  "",
    ban:            "<player> [reason…]",
    "ban-ip":       "<ip|player> [reason…]",
    pardon:         "<player>",
    "pardon-ip":    "<ip>",
    banlist:        "[ips|players]",
    kick:           "<player> [reason…]",
    tp:             "<target> [destination]",
    teleport:       "<target> [destination]",
    give:           "<player> <item> [count]",
    clear:          "[player] [item]",
    kill:           "[target]",
    gamemode:       "<mode> [player]",
    "gamemode survival":  "[player]",
    "gamemode creative":  "[player]",
    "gamemode adventure": "[player]",
    "gamemode spectator": "[player]",
    gamerule:       "<rule> [value]",
    difficulty:     "<peaceful|easy|normal|hard>",
    weather:        "<clear|rain|thunder> [duration]",
    time:           "<set|add|query> <value>",
    seed:           "",
    spawnpoint:     "[player] [pos]",
    setworldspawn:  "[pos]",
    "save-all":     "[flush]",
    "save-on":      "",
    "save-off":     "",
    stop:           "",
    reload:         "",
    xp:             "<amount> [player]",
    experience:     "<add|set|query> <player> <amount>",
    effect:         "<give|clear> <player> <effect>",
    enchant:        "<player> <enchantment> [level]",
    summon:         "<entity> [pos]",
    fill:           "<from> <to> <block>",
    setblock:       "<pos> <block>",
    locate:         "<biome|structure> <id>",
    tag:            "<target> <add|remove|list> [tag]",
    chunky:         "<start|cancel|pause|continue|world|shape|center|radius|trim|...>",
    "chunky start":    "[world] [shape] [center_x] [center_z] [radius]",
    "chunky cancel":   "",
    "chunky pause":    "",
    "chunky continue": "",
    "chunky world":    "<world>",
    "chunky shape":    "<square|circle|...>",
    "chunky center":   "<x> <z>",
    "chunky radius":   "<radius>",
    "chunky trim":     "[world] [radius] [trim_radius]",
    ftbchunks:      "<claim|unclaim|load|unload|admin>",
    ftbteams:       "<list|info|invite|...>",
    kubejs:         "<reload|hand|stages|...>",
    kjs:            "<reload|hand|stages|...>",
};

const MAX_SUGGESTIONS = 8;

let activeIndex = 0;
let currentSuggestions = [];

export function setupAutocomplete() {
    const cmdInput = document.getElementById("cmdInput");
    const cmdGhost = document.getElementById("cmdGhost");
    const cmdSuggest = document.getElementById("cmdSuggest");

    function refresh() {
        const v = cmdInput.value;
        currentSuggestions = computeAllSuggestions(v).slice(0, MAX_SUGGESTIONS);
        activeIndex = 0;

        // Inline ghost = top suggestion (only if it extends what they typed)
        const top = currentSuggestions[0];
        if (top && top.text.startsWith(v) && top.text !== v) {
            const suffix = top.text.substring(v.length);
            cmdGhost.innerHTML = `<span class="typed">${escapeHtml(v)}</span>${escapeHtml(suffix)}`;
            cmdInput.dataset.suggestion = top.text;
        } else {
            cmdGhost.innerHTML = "";
            cmdInput.dataset.suggestion = "";
        }

        renderList(cmdSuggest, currentSuggestions);
    }

    cmdInput.addEventListener("input", refresh);
    cmdInput.addEventListener("focus", refresh);
    cmdInput.addEventListener("blur", () => {
        // Delay so a click on a list item registers before we hide
        setTimeout(() => cmdSuggest.classList.remove("show"), 150);
    });

    cmdInput.addEventListener("keydown", e => {
        if (e.key === "Tab") {
            const sug = currentSuggestions[activeIndex];
            if (sug) {
                e.preventDefault();
                cmdInput.value = sug.text + " ";
                refresh();
            }
        } else if (e.key === "Escape") {
            cmdGhost.innerHTML = "";
            cmdInput.dataset.suggestion = "";
            cmdSuggest.classList.remove("show");
        } else if (e.key === "ArrowDown" && cmdSuggest.classList.contains("show") && currentSuggestions.length > 0) {
            e.preventDefault();
            activeIndex = (activeIndex + 1) % currentSuggestions.length;
            highlightActive(cmdSuggest);
        } else if (e.key === "ArrowUp" && cmdSuggest.classList.contains("show") && currentSuggestions.length > 0) {
            e.preventDefault();
            activeIndex = (activeIndex - 1 + currentSuggestions.length) % currentSuggestions.length;
            highlightActive(cmdSuggest);
        }
        // Note: Enter is handled by console.js (sends the command)
    });

    cmdSuggest.addEventListener("mousedown", e => {
        // mousedown (not click) so we beat the input blur handler
        const item = e.target.closest(".suggest-item");
        if (!item) return;
        e.preventDefault();
        const idx = parseInt(item.dataset.idx, 10);
        const sug = currentSuggestions[idx];
        if (sug) {
            cmdInput.value = sug.text + " ";
            cmdInput.focus();
            refresh();
        }
    });
}

function highlightActive(listEl) {
    [...listEl.querySelectorAll(".suggest-item")].forEach((el, i) => {
        el.classList.toggle("active", i === activeIndex);
        if (i === activeIndex) el.scrollIntoView({ block: "nearest" });
    });
}

function renderList(listEl, suggestions) {
    if (suggestions.length === 0) {
        listEl.classList.remove("show");
        listEl.innerHTML = "";
        return;
    }
    listEl.innerHTML = suggestions.map((s, i) => {
        const args = s.args ? `<span class="args">${escapeHtml(s.args)}</span>` : "";
        return `<div class="suggest-item${i === activeIndex ? " active" : ""}" data-idx="${i}">` +
               `<span>${escapeHtml(s.text)}</span>${args}</div>`;
    }).join("");
    listEl.classList.add("show");
}

// Returns an array of {text, args} suggestions ordered by relevance.
// args is the MC-style hint shown next to the name.
function computeAllSuggestions(input) {
    if (!input) return [];
    const stripped = input.startsWith("/") ? input.substring(1) : input;
    const tokens = stripped.split(" ");
    const partial = tokens[tokens.length - 1].toLowerCase();
    const completed = tokens.slice(0, -1);
    const prefix = input.startsWith("/") ? "/" : "";

    // First token: command name
    if (completed.length === 0) {
        const matches = COMMANDS.filter(c => c.startsWith(partial)).sort();
        return matches.map(name => ({
            text: prefix + name,
            args: SIGNATURES[name] ?? "",
        }));
    }

    // Subcommands
    const headLower = completed[0].toLowerCase();
    if (completed.length === 1 && SUBCOMMANDS[headLower]) {
        const subs = SUBCOMMANDS[headLower].filter(s => s.startsWith(partial));
        return subs.map(sub => ({
            text: prefix + [...completed, sub].join(" "),
            args: SIGNATURES[`${headLower} ${sub}`] ?? "",
        }));
    }

    // Player-name positions
    const cmdKey = completed.join(" ").toLowerCase();
    const playerSlot = TAKES_PLAYER_AT[cmdKey];
    if (playerSlot !== undefined && tokens.length === playerSlot + 1) {
        const matches = state.knownPlayers.filter(p => p.toLowerCase().startsWith(partial));
        return matches.map(p => ({
            text: prefix + [...completed, p].join(" "),
            args: "",
        }));
    }

    // No structured suggestion at this position -- but still show the current
    // command's signature as a contextual hint
    const sig = SIGNATURES[cmdKey];
    if (sig) {
        return [{ text: input, args: sig }];
    }
    return [];
}
