import { cmd } from "@/cli/cmd/cmd"
import { tui } from "./app"
import { Rpc } from "@/util/rpc"
import { type rpc } from "./worker"
import { Session } from "@/session"
import { bootstrap } from "@/cli/bootstrap"
import path from "path"
import { UI } from "@/cli/ui"

declare global {
  const OPENCODE_WORKER_PATH: string
}

export const TuiThreadCommand = cmd({
  command: "$0 [project]",
  describe: "start opencode tui",
  builder: (yargs) =>
    yargs
      .positional("project", {
        type: "string",
        describe: "path to start opencode in",
      })
      .option("model", {
        type: "string",
        alias: ["m"],
        describe: "model to use in the format of provider/model",
      })
      .option("continue", {
        alias: ["c"],
        describe: "continue the last session",
        type: "boolean",
      })
      .option("session", {
        alias: ["s"],
        describe: "session id to continue",
        type: "string",
      })
      .option("prompt", {
        alias: ["p"],
        type: "string",
        describe: "prompt to use",
      })
      .option("agent", {
        type: "string",
        describe: "agent to use",
      })
      .option("port", {
        type: "number",
        describe: "port to listen on",
        default: 0,
      })
      .option("hostname", {
        type: "string",
        describe: "hostname to listen on",
        default: "127.0.0.1",
      }),
  handler: async (args) => {
    const prompt = await (async () => {
      const piped = !process.stdin.isTTY ? await Bun.stdin.text() : undefined
      if (!args.prompt) return piped
      return piped ? piped + "\n" + args.prompt : args.prompt
    })()

    // Resolve relative paths against PWD to preserve behavior when using --cwd flag
    const baseCwd = process.env.PWD ?? process.cwd()
    const cwd = args.project ? path.resolve(baseCwd, args.project) : process.cwd()
    let workerPath: string | URL = new URL("./worker.ts", import.meta.url)

    if (typeof OPENCODE_WORKER_PATH !== "undefined") {
      workerPath = OPENCODE_WORKER_PATH
    }
    try {
      process.chdir(cwd)
    } catch (e) {
      UI.error("Failed to change directory to " + cwd)
      return
    }

    await bootstrap(cwd, async () => {
      const sessionID = await (async () => {
        if (args.continue) {
          const it = Session.list()
          try {
            for await (const s of it) {
              if (s.parentID === undefined) {
                return s.id
              }
            }
            return
          } finally {
            await it.return()
          }
        }
        if (args.session) {
          return args.session
        }
        return undefined
      })()

      const worker = new Worker(workerPath, {
        env: Object.fromEntries(
          Object.entries(process.env).filter(
            (entry): entry is [string, string] => entry[1] !== undefined,
          ),
        ),
      })
      worker.onerror = console.error
      const client = Rpc.client<typeof rpc>(worker)
      process.on("uncaughtException", (e) => {
        console.error(e)
      })
      process.on("unhandledRejection", (e) => {
        console.error(e)
      })
      const server = await client.call("server", {
        port: args.port,
        hostname: args.hostname,
      })
      await tui({
        url: server.url,
        sessionID,
        model: args.model,
        agent: args.agent,
        prompt,
        onExit: async () => {
          await client.call("shutdown", undefined)
        },
      })
    })
  },
})
