import { describe, expect, it } from "vitest";
import { parseStandardSetUnsetSlashCommand } from "./commands-setunset-standard.js";
import {
  parseSetUnsetCommand,
  parseSetUnsetCommandAction,
  parseSlashCommandWithSetUnset,
} from "./commands-setunset.js";

type ParsedSetUnsetAction =
  | { action: "set"; path: string; value: unknown }
  | { action: "unset"; path: string }
  | { action: "error"; message: string };

function createActionMappers() {
  return {
    onSet: (path: string, value: unknown): ParsedSetUnsetAction => ({ action: "set", path, value }),
    onUnset: (path: string): ParsedSetUnsetAction => ({ action: "unset", path }),
    onError: (message: string): ParsedSetUnsetAction => ({ action: "error", message }),
  };
}

function createSlashParams(params: {
  raw: string;
  onKnownAction?: (action: string) => ParsedSetUnsetAction | undefined;
}) {
  return {
    raw: params.raw,
    slash: "/config",
    invalidMessage: "Invalid /config syntax.",
    usageMessage: "Usage: /config show|set|unset",
    onKnownAction: params.onKnownAction ?? (() => undefined),
    ...createActionMappers(),
  };
}

describe("parseSetUnsetCommand", () => {
  it("parses unset values", () => {
    expect(
      parseSetUnsetCommand({
        slash: "/config",
        action: "unset",
        args: "foo.bar",
      }),
    ).toEqual({ kind: "unset", path: "foo.bar" });
  });

  it("parses set values", () => {
    expect(
      parseSetUnsetCommand({
        slash: "/config",
        action: "set",
        args: 'foo.bar={"x":1}',
      }),
    ).toEqual({ kind: "set", path: "foo.bar", value: { x: 1 } });
  });
});

describe("parseSetUnsetCommandAction", () => {
  it("returns null for non set/unset actions", () => {
    const mappers = createActionMappers();
    const result = parseSetUnsetCommandAction<ParsedSetUnsetAction>({
      slash: "/config",
      action: "show",
      args: "",
      ...mappers,
    });
    expect(result).toBeNull();
  });

  it("maps parse errors through onError", () => {
    const mappers = createActionMappers();
    const result = parseSetUnsetCommandAction<ParsedSetUnsetAction>({
      slash: "/config",
      action: "set",
      args: "",
      ...mappers,
    });
    expect(result).toEqual({ action: "error", message: "Usage: /config set path=value" });
  });
});

describe("parseSlashCommandWithSetUnset", () => {
  it("returns null when the input does not match the slash command", () => {
    const result = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>(
      createSlashParams({ raw: "/debug show" }),
    );
    expect(result).toBeNull();
  });

  it("prefers set/unset mapping and falls back to known actions", () => {
    const setResult = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>(
      createSlashParams({
        raw: '/config set a.b={"ok":true}',
      }),
    );
    expect(setResult).toEqual({ action: "set", path: "a.b", value: { ok: true } });

    const showResult = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>(
      createSlashParams({
        raw: "/config show",
        onKnownAction: (action) =>
          action === "show" ? { action: "unset", path: "dummy" } : undefined,
      }),
    );
    expect(showResult).toEqual({ action: "unset", path: "dummy" });
  });

  it("returns onError for unknown actions", () => {
    const unknownAction = parseSlashCommandWithSetUnset<ParsedSetUnsetAction>(
      createSlashParams({
        raw: "/config whoami",
      }),
    );
    expect(unknownAction).toEqual({ action: "error", message: "Usage: /config show|set|unset" });
  });
});

describe("parseStandardSetUnsetSlashCommand", () => {
  it("uses default set/unset/error mappings", () => {
    const result = parseStandardSetUnsetSlashCommand<ParsedSetUnsetAction>({
      raw: '/config set a.b={"ok":true}',
      slash: "/config",
      invalidMessage: "Invalid /config syntax.",
      usageMessage: "Usage: /config show|set|unset",
      onKnownAction: () => undefined,
    });
    expect(result).toEqual({ action: "set", path: "a.b", value: { ok: true } });
  });

  it("supports caller-provided mappings", () => {
    const result = parseStandardSetUnsetSlashCommand<ParsedSetUnsetAction>({
      raw: "/config unset a.b",
      slash: "/config",
      invalidMessage: "Invalid /config syntax.",
      usageMessage: "Usage: /config show|set|unset",
      onKnownAction: () => undefined,
      onUnset: (path) => ({ action: "unset", path: `wrapped:${path}` }),
    });
    expect(result).toEqual({ action: "unset", path: "wrapped:a.b" });
  });
});
