import { Bus } from "@/bus"
import { TuiEvent } from "@/cli/cmd/tui/event"
import { Instance } from "@/project/instance"
import { Session } from "@/session/session"
import { SessionID, MessageID, PartID } from "@/session/schema"
import { SessionStatus } from "@/session/status"
import { MessageV2 } from "@/session/message-v2"
import * as Log from "@saeeol/core/util/log"
import { pending, PlanFollowupRuntime } from "./plan-followup-runtime"
import { formatTodos, generateHandover } from "./plan-followup-handover"
import { SaeeolSessionPromptQueue } from "@/saeeol/session/prompt-queue"

const log = Log.create({ service: "plan.followup" })

export async function startNew(input: {
  sessionID: SessionID
  plan: string
  messages: MessageV2.WithParts[]
  model: MessageV2.User["model"]
  abort?: AbortSignal
  resolveCodeModel: (m: Pick<MessageV2.User, "model">) => Promise<{ model: MessageV2.User["model"] }>
}) {
  const code = await input.resolveCodeModel({ model: input.model })
  const session = await Session.get(input.sessionID)

  await Instance.provide({
    directory: session.directory,
    fn: async () => {
      const next = await Session.create({})
      const ctl = new AbortController()
      pending.set(next.id, ctl)
      await SessionStatus.set(next.id, { type: "busy" })
      await Bus.publish(TuiEvent.SessionSelect, { sessionID: next.id })

      const idle = () =>
        SessionStatus.set(next.id, { type: "idle" }).catch((err) => {
          log.warn("failed to clear follow-up busy status", { sessionID: next.id, err })
        })

      try {
        const file = Session.plan(session, Instance.current)
        const todos = await PlanFollowupRuntime.todo.get(input.sessionID)
        const todoList = formatTodos(todos)

        const compose = (handover: string) => {
          const sections = [
            `Plan file: ${file}\nRead this file first and treat it as the source of truth for implementation.`,
            `Implement the following plan:\n\n${input.plan}`,
          ]
          if (handover) sections.push(`## Handover from Planning Session\n\n${handover}`)
          if (todoList) sections.push(`## Todo List\n\n${todoList}`)
          return sections.join("\n\n")
        }

        const msg: MessageV2.User = {
          id: MessageID.ascending(),
          sessionID: next.id,
          role: "user",
          time: { created: Date.now() },
          agent: "code",
          model: code.model,
        }
        await Session.updateMessage(msg)
        const pid = PartID.ascending()
        await Session.updatePart({
          id: pid,
          messageID: msg.id,
          sessionID: next.id,
          type: "text",
          text: compose(""),
          synthetic: false,
        } satisfies MessageV2.TextPart)

        if (todos.length) {
          await PlanFollowupRuntime.todo.update({ sessionID: next.id, todos })
        }

        const handover = await generateHandover({
          messages: input.messages,
          model: input.model,
          abort: input.abort ? AbortSignal.any([input.abort, ctl.signal]) : ctl.signal,
        })
        if (ctl.signal.aborted) { await idle(); return }

        if (handover) {
          await Session.updatePart({
            id: pid, messageID: msg.id, sessionID: next.id,
            type: "text", text: compose(handover), synthetic: false,
          } satisfies MessageV2.TextPart)
        }
        if (ctl.signal.aborted) { await idle(); return }

        const queue = Instance.provide({
          directory: next.directory,
          fn: async () => {
            if (ctl.signal.aborted) { await idle(); return }
            await PlanFollowupRuntime.loop(next.id)
          },
        })

        void queue
          .catch((error) => { log.error("failed to start follow-up session", { sessionID: next.id, error }); void idle() })
          .finally(() => { if (pending.get(next.id) === ctl) pending.delete(next.id) })
      } catch (error) {
        if (pending.get(next.id) === ctl) pending.delete(next.id)
        await idle()
        throw error
      }
    },
  })
}
