import { describe, expect, it } from "vitest";

import {
  buildCommandTextFromArgs,
  parseCommandArgs,
  resolveCommandArgMenu,
  serializeCommandArgs,
} from "./commands-registry.js";
import type { ChatCommandDefinition } from "./commands-registry.types.js";

describe("commands registry args", () => {
  it("parses positional args and captureRemaining", () => {
    const command: ChatCommandDefinition = {
      key: "debug",
      description: "debug",
      textAliases: [],
      scope: "both",
      argsParsing: "positional",
      args: [
        { name: "action", description: "action", type: "string" },
        { name: "path", description: "path", type: "string" },
        { name: "value", description: "value", type: "string", captureRemaining: true },
      ],
    };

    const args = parseCommandArgs(command, "set foo bar baz");
    expect(args?.values).toEqual({ action: "set", path: "foo", value: "bar baz" });
  });

  it("serializes args via raw first, then values", () => {
    const command: ChatCommandDefinition = {
      key: "model",
      description: "model",
      textAliases: [],
      scope: "both",
      argsParsing: "positional",
      args: [{ name: "model", description: "model", type: "string", captureRemaining: true }],
    };

    expect(serializeCommandArgs(command, { raw: "gpt-5.2-codex" })).toBe("gpt-5.2-codex");
    expect(serializeCommandArgs(command, { values: { model: "gpt-5.2-codex" } })).toBe(
      "gpt-5.2-codex",
    );
    expect(buildCommandTextFromArgs(command, { values: { model: "gpt-5.2-codex" } })).toBe(
      "/model gpt-5.2-codex",
    );
  });

  it("resolves auto arg menus when missing a choice arg", () => {
    const command: ChatCommandDefinition = {
      key: "usage",
      description: "usage",
      textAliases: [],
      scope: "both",
      argsMenu: "auto",
      argsParsing: "positional",
      args: [
        {
          name: "mode",
          description: "mode",
          type: "string",
          choices: ["off", "tokens", "full", "cost"],
        },
      ],
    };

    const menu = resolveCommandArgMenu({ command, args: undefined, cfg: {} as never });
    expect(menu?.arg.name).toBe("mode");
    expect(menu?.choices).toEqual(["off", "tokens", "full", "cost"]);
  });

  it("does not show menus when arg already provided", () => {
    const command: ChatCommandDefinition = {
      key: "usage",
      description: "usage",
      textAliases: [],
      scope: "both",
      argsMenu: "auto",
      argsParsing: "positional",
      args: [
        {
          name: "mode",
          description: "mode",
          type: "string",
          choices: ["off", "tokens", "full", "cost"],
        },
      ],
    };

    const menu = resolveCommandArgMenu({
      command,
      args: { values: { mode: "tokens" } },
      cfg: {} as never,
    });
    expect(menu).toBeNull();
  });

  it("resolves function-based choices with a default provider/model context", () => {
    let seen: { provider: string; model: string; commandKey: string; argName: string } | null =
      null;

    const command: ChatCommandDefinition = {
      key: "think",
      description: "think",
      textAliases: [],
      scope: "both",
      argsMenu: "auto",
      argsParsing: "positional",
      args: [
        {
          name: "level",
          description: "level",
          type: "string",
          choices: ({ provider, model, command, arg }) => {
            seen = { provider, model, commandKey: command.key, argName: arg.name };
            return ["low", "high"];
          },
        },
      ],
    };

    const menu = resolveCommandArgMenu({ command, args: undefined, cfg: {} as never });
    expect(menu?.arg.name).toBe("level");
    expect(menu?.choices).toEqual(["low", "high"]);
    expect(seen?.commandKey).toBe("think");
    expect(seen?.argName).toBe("level");
    expect(seen?.provider).toBeTruthy();
    expect(seen?.model).toBeTruthy();
  });

  it("does not show menus when args were provided as raw text only", () => {
    const command: ChatCommandDefinition = {
      key: "usage",
      description: "usage",
      textAliases: [],
      scope: "both",
      argsMenu: "auto",
      argsParsing: "none",
      args: [
        {
          name: "mode",
          description: "on or off",
          type: "string",
          choices: ["off", "tokens", "full", "cost"],
        },
      ],
    };

    const menu = resolveCommandArgMenu({
      command,
      args: { raw: "on" },
      cfg: {} as never,
    });
    expect(menu).toBeNull();
  });
});
