import { createOpencodeClient } from "@opencode-ai/sdk/v2"
import { RGBA, type CliRenderer } from "@opentui/core"
import { createPluginKeybind } from "../../src/cli/cmd/tui/context/plugin-keybinds"
import type { HostPluginApi } from "../../src/cli/cmd/tui/plugin/slots"

type Count = {
  event_add: number
  event_drop: number
  route_add: number
  route_drop: number
  command_add: number
  command_drop: number
}

function themeCurrent(): HostPluginApi["theme"]["current"] {
  const a = RGBA.fromInts(0, 120, 240)
  const b = RGBA.fromInts(120, 120, 120)
  const c = RGBA.fromInts(230, 230, 230)
  const d = RGBA.fromInts(120, 30, 30)
  const e = RGBA.fromInts(140, 100, 40)
  const f = RGBA.fromInts(20, 140, 80)
  const g = RGBA.fromInts(20, 80, 160)
  const h = RGBA.fromInts(40, 40, 40)
  const i = RGBA.fromInts(60, 60, 60)
  const j = RGBA.fromInts(80, 80, 80)
  return {
    primary: a,
    secondary: b,
    accent: a,
    error: d,
    warning: e,
    success: f,
    info: g,
    text: c,
    textMuted: b,
    selectedListItemText: h,
    background: h,
    backgroundPanel: h,
    backgroundElement: i,
    backgroundMenu: i,
    border: j,
    borderActive: c,
    borderSubtle: i,
    diffAdded: f,
    diffRemoved: d,
    diffContext: b,
    diffHunkHeader: b,
    diffHighlightAdded: f,
    diffHighlightRemoved: d,
    diffAddedBg: h,
    diffRemovedBg: h,
    diffContextBg: h,
    diffLineNumber: b,
    diffAddedLineNumberBg: h,
    diffRemovedLineNumberBg: h,
    markdownText: c,
    markdownHeading: c,
    markdownLink: a,
    markdownLinkText: g,
    markdownCode: f,
    markdownBlockQuote: e,
    markdownEmph: e,
    markdownStrong: c,
    markdownHorizontalRule: b,
    markdownListItem: a,
    markdownListEnumeration: g,
    markdownImage: a,
    markdownImageText: g,
    markdownCodeBlock: c,
    syntaxComment: b,
    syntaxKeyword: a,
    syntaxFunction: g,
    syntaxVariable: c,
    syntaxString: f,
    syntaxNumber: e,
    syntaxType: a,
    syntaxOperator: a,
    syntaxPunctuation: c,
    thinkingOpacity: 0.6,
  }
}

type Opts = {
  client?: HostPluginApi["client"] | (() => HostPluginApi["client"])
  scopedClient?: HostPluginApi["scopedClient"]
  workspace?: Partial<HostPluginApi["workspace"]>
  renderer?: HostPluginApi["renderer"]
  count?: Count
  keybind?: Partial<HostPluginApi["keybind"]>
  tuiConfig?: HostPluginApi["tuiConfig"]
  app?: Partial<HostPluginApi["app"]>
  state?: {
    ready?: HostPluginApi["state"]["ready"]
    config?: HostPluginApi["state"]["config"]
    provider?: HostPluginApi["state"]["provider"]
    path?: HostPluginApi["state"]["path"]
    vcs?: HostPluginApi["state"]["vcs"]
    workspace?: Partial<HostPluginApi["state"]["workspace"]>
    session?: Partial<HostPluginApi["state"]["session"]>
    part?: HostPluginApi["state"]["part"]
    lsp?: HostPluginApi["state"]["lsp"]
    mcp?: HostPluginApi["state"]["mcp"]
  }
  theme?: {
    selected?: string
    has?: HostPluginApi["theme"]["has"]
    set?: HostPluginApi["theme"]["set"]
    install?: HostPluginApi["theme"]["install"]
    mode?: HostPluginApi["theme"]["mode"]
    ready?: boolean
    current?: HostPluginApi["theme"]["current"]
  }
}

export function createTuiPluginApi(opts: Opts = {}): HostPluginApi {
  const kv: Record<string, unknown> = {}
  const count = opts.count
  const ctrl = new AbortController()
  const own = createOpencodeClient({
    baseUrl: "http://localhost:4096",
  })
  const fallback = () => own
  const read =
    typeof opts.client === "function"
      ? opts.client
      : opts.client
        ? () => opts.client as HostPluginApi["client"]
        : fallback
  const client = () => read()
  const scopedClient = opts.scopedClient ?? ((_workspaceID?: string) => client())
  const workspace: HostPluginApi["workspace"] = {
    current: opts.workspace?.current ?? (() => undefined),
    set: opts.workspace?.set ?? (() => {}),
  }
  let depth = 0
  let size: "medium" | "large" | "xlarge" = "medium"
  const has = opts.theme?.has ?? (() => false)
  let selected = opts.theme?.selected ?? "opencode"
  const key = {
    match: opts.keybind?.match ?? (() => false),
    print: opts.keybind?.print ?? ((name: string) => name),
  }
  const set =
    opts.theme?.set ??
    ((name: string) => {
      if (!has(name)) return false
      selected = name
      return true
    })
  const renderer: CliRenderer = opts.renderer ?? {
    ...Object.create(null),
    once(this: CliRenderer) {
      return this
    },
  }

  function kvGet(name: string): unknown
  function kvGet<Value>(name: string, fallback: Value): Value
  function kvGet(name: string, fallback?: unknown) {
    const value = kv[name]
    if (value === undefined) return fallback
    return value
  }

  return {
    app: {
      get version() {
        return opts.app?.version ?? "0.0.0-test"
      },
    },
    get client() {
      return client()
    },
    scopedClient,
    workspace,
    event: {
      on: () => {
        if (count) count.event_add += 1
        return () => {
          if (!count) return
          count.event_drop += 1
        }
      },
    },
    renderer,
    slots: {
      register: () => "fixture-slot",
    },
    plugins: {
      list: () => [],
      activate: async () => false,
      deactivate: async () => false,
      add: async () => false,
      install: async () => ({
        ok: false,
        message: "not implemented in fixture",
      }),
    },
    lifecycle: {
      signal: ctrl.signal,
      onDispose() {
        return () => {}
      },
    },
    command: {
      register: () => {
        if (count) count.command_add += 1
        return () => {
          if (!count) return
          count.command_drop += 1
        }
      },
      trigger: () => {},
    },
    route: {
      register: () => {
        if (count) count.route_add += 1
        return () => {
          if (!count) return
          count.route_drop += 1
        }
      },
      navigate: () => {},
      get current() {
        return { name: "home" }
      },
    },
    ui: {
      Dialog: () => null,
      DialogAlert: () => null,
      DialogConfirm: () => null,
      DialogPrompt: () => null,
      DialogSelect: () => null,
      toast: () => {},
      dialog: {
        replace: () => {
          depth = 1
        },
        clear: () => {
          depth = 0
          size = "medium"
        },
        setSize: (next) => {
          size = next
        },
        get size() {
          return size
        },
        get depth() {
          return depth
        },
        get open() {
          return depth > 0
        },
      },
    },
    keybind: {
      ...key,
      create:
        opts.keybind?.create ??
        ((defaults, over) => {
          return createPluginKeybind(key, defaults, over)
        }),
    },
    tuiConfig: opts.tuiConfig ?? {},
    kv: {
      get: kvGet,
      set(name, value) {
        kv[name] = value
      },
      get ready() {
        return true
      },
    },
    state: {
      get ready() {
        return opts.state?.ready ?? true
      },
      get config() {
        return opts.state?.config ?? {}
      },
      get provider() {
        return opts.state?.provider ?? []
      },
      get path() {
        return opts.state?.path ?? { state: "", config: "", worktree: "", directory: "" }
      },
      get vcs() {
        return opts.state?.vcs
      },
      workspace: {
        list: opts.state?.workspace?.list ?? (() => []),
        get: opts.state?.workspace?.get ?? (() => undefined),
      },
      session: {
        count: opts.state?.session?.count ?? (() => 0),
        diff: opts.state?.session?.diff ?? (() => []),
        todo: opts.state?.session?.todo ?? (() => []),
        messages: opts.state?.session?.messages ?? (() => []),
        status: opts.state?.session?.status ?? (() => undefined),
        permission: opts.state?.session?.permission ?? (() => []),
        question: opts.state?.session?.question ?? (() => []),
      },
      part: opts.state?.part ?? (() => []),
      lsp: opts.state?.lsp ?? (() => []),
      mcp: opts.state?.mcp ?? (() => []),
    },
    theme: {
      get current() {
        return opts.theme?.current ?? themeCurrent()
      },
      get selected() {
        return selected
      },
      has(name) {
        return has(name)
      },
      set(name) {
        return set(name)
      },
      async install(file) {
        if (opts.theme?.install) return opts.theme.install(file)
        throw new Error("base theme.install should not run")
      },
      mode() {
        if (opts.theme?.mode) return opts.theme.mode()
        return "dark"
      },
      get ready() {
        return opts.theme?.ready ?? true
      },
    },
  }
}
