import { Argument, Flag } from "effect/unstable/cli"
import { Spec } from "../framework/spec"

declare const OPENCODE_CLI_NAME: string | undefined

const MiniParams = {
  continue: Flag.boolean("continue").pipe(
    Flag.withAlias("c"),
    Flag.withDescription("Continue the last session"),
    Flag.withDefault(false),
  ),
  session: Flag.string("session").pipe(
    Flag.withAlias("s"),
    Flag.withDescription("Session ID to continue"),
    Flag.optional,
  ),
  fork: Flag.boolean("fork").pipe(
    Flag.withDescription("Fork the session when continuing"),
    Flag.withDefault(false),
  ),
  replay: Flag.boolean("replay").pipe(
    Flag.withDescription("Replay session history on resume and after resize"),
    Flag.withDefault(true),
  ),
  replayLimit: Flag.integer("replay-limit").pipe(
    Flag.withDescription("Cap visible replay to the newest N messages"),
    Flag.optional,
  ),
}

export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCODE_CLI_NAME : "opencode", {
  description: "OpenCode 2.0 preview command line interface",
  params: {
    directory: Argument.string("directory").pipe(
      Argument.withDescription("Directory to start OpenCode in"),
      Argument.optional,
    ),
    standalone: Flag.boolean("standalone").pipe(
      Flag.withDescription("Run with a private server instead of the background service"),
      Flag.withDefault(false),
    ),
    server: Flag.string("server").pipe(
      Flag.withDescription("Connect to a server URL instead of the background service"),
      Flag.optional,
    ),
    continue: Flag.boolean("continue").pipe(
      Flag.withAlias("c"),
      Flag.withDescription("Continue the last session"),
      Flag.withDefault(false),
    ),
    session: Flag.string("session").pipe(
      Flag.withAlias("s"),
      Flag.withDescription("Session ID to continue"),
      Flag.optional,
    ),
  },
  commands: [
    Spec.make("api", {
      description: "Make a request to the running server",
      params: {
        request: Argument.string("operation | method path").pipe(
          Argument.withDescription("OpenAPI operation ID, or an HTTP method followed by a path"),
          Argument.variadic({ min: 1, max: 2 }),
        ),
        data: Flag.string("data").pipe(Flag.withAlias("d"), Flag.withDescription("Request body"), Flag.optional),
        header: Flag.string("header").pipe(
          Flag.withAlias("H"),
          Flag.withDescription("Request header in name:value form"),
          Flag.atMost(100),
        ),
        param: Flag.keyValuePair("param").pipe(Flag.withDescription("OpenAPI path or query parameter"), Flag.optional),
      },
    }),
    Spec.make("debug", {
      description: "Debugging and troubleshooting tools",
      commands: [Spec.make("agents", { description: "List all agents" })],
    }),
    Spec.make("mcp", {
      description: "Manage MCP (Model Context Protocol) servers",
      commands: [
        Spec.make("list", { description: "List configured MCP servers and their status" }),
        Spec.make("add", {
          description: "Add an MCP server to your configuration",
          params: {
            name: Argument.string("name").pipe(Argument.withDescription("Name of the MCP server")),
            command: Argument.string("command").pipe(
              Argument.withDescription("Command and arguments for a local server, passed after --"),
              Argument.variadic({ min: 0 }),
            ),
            url: Flag.string("url").pipe(Flag.withDescription("URL for a remote MCP server"), Flag.optional),
            header: Flag.keyValuePair("header").pipe(
              Flag.withDescription("HTTP header for a remote server, as name=value"),
              Flag.optional,
            ),
            env: Flag.keyValuePair("env").pipe(
              Flag.withDescription("Environment variable for a local server, as name=value"),
              Flag.optional,
            ),
            global: Flag.boolean("global").pipe(
              Flag.withDescription("Write to the global config instead of the project config"),
              Flag.withDefault(false),
            ),
          },
        }),
        Spec.make("auth", {
          description: "Authenticate with an OAuth-capable remote MCP server",
          params: { name: Argument.string("name").pipe(Argument.withDescription("Name of the MCP server")) },
        }),
        Spec.make("logout", {
          description: "Remove stored OAuth credentials for an MCP server",
          params: { name: Argument.string("name").pipe(Argument.withDescription("Name of the MCP server")) },
        }),
      ],
    }),
    Spec.make("migrate", { description: "Migrate v1 data to v2" }),
    Spec.make("mini", {
      description: "Start the minimal interactive interface",
      params: {
        ...MiniParams,
        project: Argument.string("project").pipe(
          Argument.withDescription("Path to start OpenCode in"),
          Argument.optional,
        ),
        model: Flag.string("model").pipe(
          Flag.withAlias("m"),
          Flag.withDescription("Model to use in the format provider/model"),
          Flag.optional,
        ),
        agent: Flag.string("agent").pipe(Flag.withDescription("Agent to use"), Flag.optional),
        prompt: Flag.string("prompt").pipe(Flag.withDescription("Prompt to use"), Flag.optional),
        server: Flag.string("server").pipe(
          Flag.withDescription("Connect to a server URL instead of the background service"),
          Flag.optional,
        ),
        demo: Flag.boolean("demo").pipe(Flag.withDefault(false), Flag.withHidden),
      },
    }),
    Spec.make("run", {
      description: "Run OpenCode with a message",
      params: {
        message: Argument.string("message").pipe(
          Argument.withDescription("Message to send"),
          Argument.variadic({ min: 0 }),
        ),
        continue: Flag.boolean("continue").pipe(
          Flag.withAlias("c"),
          Flag.withDescription("Continue the last session"),
          Flag.withDefault(false),
        ),
        session: Flag.string("session").pipe(
          Flag.withAlias("s"),
          Flag.withDescription("Session ID to continue"),
          Flag.optional,
        ),
        fork: Flag.boolean("fork").pipe(
          Flag.withDescription("Fork the session before continuing"),
          Flag.withDefault(false),
        ),
        model: Flag.string("model").pipe(
          Flag.withAlias("m"),
          Flag.withDescription("Model to use in the format provider/model"),
          Flag.optional,
        ),
        agent: Flag.string("agent").pipe(Flag.withDescription("Agent to use"), Flag.optional),
        format: Flag.choice("format", ["default", "json"]).pipe(
          Flag.withDescription("Output format"),
          Flag.withDefault("default"),
        ),
        file: Flag.string("file").pipe(
          Flag.withAlias("f"),
          Flag.withDescription("File to attach to the message"),
          Flag.atMost(100),
        ),
        title: Flag.string("title").pipe(Flag.withDescription("Session title"), Flag.optional),
        server: Flag.string("server").pipe(
          Flag.withDescription("Connect to a server URL instead of the background service"),
          Flag.optional,
        ),
        dir: Flag.string("dir").pipe(Flag.withDescription("Directory to run in"), Flag.optional),
        variant: Flag.string("variant").pipe(Flag.withDescription("Model variant"), Flag.optional),
        thinking: Flag.boolean("thinking").pipe(
          Flag.withDescription("Show thinking blocks"),
          Flag.withDefault(false),
        ),
        auto: Flag.boolean("auto").pipe(
          Flag.withDescription("Auto-approve permissions that are not explicitly denied"),
          Flag.withDefault(false),
        ),
        yolo: Flag.boolean("yolo").pipe(Flag.withDefault(false), Flag.withHidden),
        dangerouslySkipPermissions: Flag.boolean("dangerously-skip-permissions").pipe(
          Flag.withDefault(false),
          Flag.withHidden,
        ),
      },
    }),
    Spec.make("service", {
      description: "Manage the background server",
      commands: [
        Spec.make("start", { description: "Start the background server" }),
        Spec.make("restart", { description: "Restart the background server" }),
        Spec.make("status", { description: "Show background server status" }),
        Spec.make("stop", { description: "Stop the background server" }),
        Spec.make("get", {
          description: "Get service configuration",
          params: { key: Argument.string("key").pipe(Argument.optional) },
        }),
        Spec.make("set", {
          description: "Set service configuration",
          params: { key: Argument.string("key"), value: Argument.string("value") },
        }),
        Spec.make("unset", {
          description: "Unset service configuration",
          params: { key: Argument.string("key") },
        }),
      ],
    }),
    Spec.make("serve", {
      description: "Start the v2 API server",
      params: {
        hostname: Flag.string("hostname").pipe(Flag.optional),
        port: Flag.integer("port").pipe(Flag.optional),
        service: Flag.boolean("service").pipe(Flag.withDefault(false)),
        stdio: Flag.boolean("stdio").pipe(Flag.withDefault(false)),
      },
    }),
  ],
})
