import type { V2Event } from "@opencode-ai/sdk/v2"
import type { TuiAttentionSoundName, TuiPlugin, TuiPluginApi } from "@opencode-ai/plugin/tui"
import type { BuiltinTuiPlugin } from "../builtins"

const id = "internal:notifications"

type SessionError = Extract<V2Event, { type: "session.error" }>["data"]["error"]

function notify(api: TuiPluginApi, sessionID: string | undefined, message: string, sound: TuiAttentionSoundName) {
  const session = sessionID ? api.state.session.get(sessionID) : undefined
  const isSubagent = session?.parentID !== undefined
  void api.attention.notify({
    title: session?.title,
    message,
    notification: isSubagent ? false : { when: "blurred" },
    sound: { name: sound, when: "always" },
  })
}

function sessionErrorMessage(error: SessionError) {
  if (error?.name === "MessageAbortedError") return "Session aborted"
  const data = error?.data
  if (data && typeof data === "object" && "message" in data && data.message === "SSE read timed out") {
    return "Model stopped responding"
  }
  return "Session error"
}

const tui: TuiPlugin = async (api) => {
  const active = new Set<string>()
  const errored = new Set<string>()
  const forms = new Set<string>()
  const questions = new Set<string>()
  const permissions = new Set<string>()

  api.event.on("form.created", (event) => {
    if (event.data.form.sessionID === "global") return
    if (forms.has(event.data.form.id)) return
    forms.add(event.data.form.id)
    notify(api, event.data.form.sessionID, "Input needs response", "question")
  })

  api.event.on("form.replied", (event) => {
    forms.delete(event.data.id)
  })

  api.event.on("form.cancelled", (event) => {
    forms.delete(event.data.id)
  })

  api.event.on("question.asked", (event) => {
    if (questions.has(event.data.id)) return
    questions.add(event.data.id)
    notify(api, event.data.sessionID, "Question needs input", "question")
  })

  api.event.on("question.replied", (event) => {
    questions.delete(event.data.requestID)
  })

  api.event.on("question.rejected", (event) => {
    questions.delete(event.data.requestID)
  })

  api.event.on("permission.asked", (event) => {
    if (permissions.has(event.data.id)) return
    permissions.add(event.data.id)
    notify(api, event.data.sessionID, "Permission needs input", "permission")
  })

  api.event.on("permission.replied", (event) => {
    permissions.delete(event.data.requestID)
  })

  const started = (sessionID: string) => {
    active.add(sessionID)
    errored.delete(sessionID)
  }

  const ended = (sessionID: string) => {
    if (!active.has(sessionID)) return
    active.delete(sessionID)

    if (errored.has(sessionID)) {
      errored.delete(sessionID)
      return
    }

    const session = api.state.session.get(sessionID)
    notify(api, sessionID, "Session done", session?.parentID ? "subagent_done" : "done")
  }

  api.event.on("session.prompt.promoted", (event) => started(event.data.sessionID))
  api.event.on("session.shell.started", (event) => started(event.data.sessionID))
  api.event.on("session.step.started", (event) => started(event.data.sessionID))
  api.event.on("session.retried", (event) => started(event.data.sessionID))
  api.event.on("session.compaction.started", (event) => started(event.data.sessionID))
  api.event.on("session.shell.ended", (event) => ended(event.data.sessionID))
  api.event.on("session.step.ended", (event) => {
    if (event.data.finish === "tool-calls") return
    ended(event.data.sessionID)
  })
  api.event.on("session.step.failed", (event) => {
    const sessionID = event.data.sessionID
    if (!active.has(sessionID)) return
    errored.add(sessionID)
    notify(api, sessionID, "Session error", "error")
    ended(sessionID)
  })

  api.event.on("session.error", (event) => {
    const sessionID = event.data.sessionID
    if (!sessionID) return
    if (!active.has(sessionID)) return
    errored.add(sessionID)
    notify(api, sessionID, sessionErrorMessage(event.data.error), "error")
  })
}

const plugin: BuiltinTuiPlugin = {
  id,
  tui,
}

export default plugin
