/** @jsxImportSource @opentui/solid */
/**
 * infinicode — mesh command palette
 *
 * Adds the OpenKernel mesh/kernel slash-commands natively to the TUI's `/`
 * command palette. Each command is dispatched to a LOCAL mesh node over HTTP
 * (POST /fed/command → kernel.command()), so the same command surface exposed by
 * `infinicode serve`'s console and the CLI is available right inside the TUI.
 *
 * The local node is started (or reused) by `infinicode run`, which injects:
 *   INFINICODE_MESH_URL   e.g. http://127.0.0.1:47913
 *   INFINICODE_MESH_TOKEN optional bearer token
 *
 * Argless commands (/nodes, /running, /activity, /whoami, /mesh-help) run
 * immediately and show output in an alert. Arg-taking commands (/node, /build,
 * /follow, /dispatch, /goal, /mission, /role) open a prompt first.
 */
import type { TuiPlugin, TuiPluginApi, TuiPluginModule } from "@opencode-ai/plugin/tui"

type CommandResult = { ok?: boolean; text?: string; data?: unknown }

const meshUrl = () => (process.env.INFINICODE_MESH_URL ?? "http://127.0.0.1:47913").replace(/\/+$/, "")
const meshToken = () => process.env.INFINICODE_MESH_TOKEN

/** POST a slash-command to the local mesh node and return its CommandResult. */
async function send(input: string): Promise<CommandResult> {
  const token = meshToken()
  try {
    const res = await fetch(`${meshUrl()}/fed/command`, {
      method: "POST",
      headers: {
        "content-type": "application/json",
        ...(token ? { authorization: `Bearer ${token}` } : {}),
      },
      body: JSON.stringify({ input }),
      signal: AbortSignal.timeout(30_000),
    })
    if (!res.ok) return { ok: false, text: `mesh error ${res.status} — is a node running? (infinicode serve)` }
    return (await res.json()) as CommandResult
  } catch (e) {
    const msg = e instanceof Error ? e.message : String(e)
    return { ok: false, text: `no local mesh node reachable at ${meshUrl()} (${msg})` }
  }
}

/** Render a CommandResult in a scrollable alert dialog. */
function show(api: TuiPluginApi, title: string, res: CommandResult): void {
  const DialogAlert = api.ui.DialogAlert
  const message = (res.text && res.text.trim()) || (res.ok ? "(no output)" : "command failed")
  api.ui.dialog.setSize("large")
  api.ui.dialog.replace(() => (
    <DialogAlert title={title} message={message} onConfirm={() => api.ui.dialog.clear()} />
  ))
}

/** Run a fixed slash-command now and show the result. */
function runNow(api: TuiPluginApi, title: string, input: string): void {
  api.ui.toast({ variant: "info", title: "mesh", message: `${input} …`, duration: 1200 })
  void send(input).then((res) => show(api, title, res))
}

/** Open a prompt, then dispatch `<slash> <entered args>`. */
function runWithArgs(
  api: TuiPluginApi,
  title: string,
  slash: string,
  placeholder: string,
): void {
  const DialogPrompt = api.ui.DialogPrompt
  api.ui.dialog.setSize("medium")
  api.ui.dialog.replace(() => (
    <DialogPrompt
      title={title}
      value=""
      placeholder={placeholder}
      onConfirm={(args: string) => {
        api.ui.dialog.clear()
        const input = args && args.trim() ? `${slash} ${args.trim()}` : slash
        api.ui.toast({ variant: "info", title: "mesh", message: `${input} …`, duration: 1200 })
        void send(input).then((res) => show(api, title, res))
      }}
      onCancel={() => api.ui.dialog.clear()}
    />
  ))
}

interface Spec {
  name: string
  slashName: string
  title: string
  /** if set, prompt for args first with this placeholder; else run immediately */
  arg?: string
  /** the underlying kernel slash-command (defaults to `/${slashName}`) */
  cmd?: string
}

const SPECS: Spec[] = [
  { name: "mesh_nodes", slashName: "nodes", title: "Mesh nodes" },
  { name: "mesh_running", slashName: "running", title: "Running tasks" },
  { name: "mesh_activity", slashName: "activity", title: "Mesh activity" },
  { name: "mesh_whoami", slashName: "whoami", title: "This node" },
  { name: "mesh_help", slashName: "mesh-help", title: "Mesh commands", cmd: "/help" },
  { name: "mesh_node", slashName: "node", title: "Node control", arg: "<node> <message>", cmd: "/node" },
  { name: "mesh_build", slashName: "build", title: "Phased build", arg: "<node> <goal>", cmd: "/build" },
  { name: "mesh_dispatch", slashName: "dispatch", title: "Dispatch task", arg: "<node> <task>", cmd: "/dispatch" },
  { name: "mesh_follow", slashName: "follow", title: "Follow run", arg: "<run-id>", cmd: "/follow" },
  { name: "mesh_goal", slashName: "goal", title: "New goal", arg: "<goal description>", cmd: "/goal" },
  { name: "mesh_mission", slashName: "mission", title: "Mission", arg: "<mission description>", cmd: "/mission" },
  { name: "mesh_role", slashName: "role", title: "Set role", arg: "<role>", cmd: "/role" },
]

const tui: TuiPlugin = async (api, options) => {
  if (options?.enabled === false) return

  // No keybindings — these are palette-only slash commands. Empty bindings avoids
  // any dependency on the keymap-extras runtime module (which may not resolve for
  // an externally-loaded plugin) so registration can never fail to load.
  api.keymap.registerLayer({
    commands: SPECS.map((s) => ({
      name: s.name,
      title: s.title,
      category: "Mesh",
      namespace: "palette" as const,
      slashName: s.slashName,
      run() {
        const cmd = s.cmd ?? `/${s.slashName}`
        if (s.arg) runWithArgs(api, s.title, cmd, s.arg)
        else runNow(api, s.title, cmd)
      },
    })),
    bindings: [],
  })
}

const plugin: TuiPluginModule & { id: string } = {
  id: "infinicode-mesh-commands",
  tui,
}

export default plugin
