globalThis.AI_SDK_LOG_WARNINGS = false

import { Effect, Layer, Scope, Latch, Context } from "effect"
import { ChildProcessSpawner } from "effect/unstable/process"
import { CrossSpawnSpawner } from "@saeeol/core/cross-spawn-spawner"
import { SaeeolSessionPrompt } from "@/saeeol/session/prompt"
import { SaeeolSessionPromptQueue } from "@/saeeol/session/prompt-queue"
import { SaeeolSession } from "@/saeeol/session"
import { SaeeolSessionProcessor } from "@/saeeol/session/processor"
import { Suggestion } from "@/saeeol/suggestion"
import { Question } from "@/question"
import { SessionID, MessageID } from "../core/schema"
import { MessageV2 } from "../message/message-v2"
import { makeRuntime } from "@/effect/run-service"
import { InstanceState } from "@/effect/instance-state"
import { EffectBridge } from "@/effect/bridge"
import { SessionRunState } from "../core/run-state"
import { SessionStatus } from "../core/status"
import { SessionCompaction } from "../core/compaction/compaction"
import { SessionProcessor } from "../core/processor"
import { Command } from "../../command"
import { Permission } from "@/permission"
import { MCP } from "../../mcp"
import { LSP } from "@/lsp/lsp"
import { ToolRegistry } from "@/tool/registry"
import { Truncate } from "@/tool/truncate"
import { Provider } from "@/provider/provider"
import { Config } from "@/config/config"
import { Instruction } from "../core/instruction"
import { AppFileSystem } from "@saeeol/core/filesystem"
import { Plugin } from "../../plugin"
import { Session as SessionMod } from "../core/session"
import { SessionRevert } from "../core/revert"
import { SessionSummary } from "../core/summary"
import { Agent } from "../../agent/agent"
import { SystemPrompt } from "../core/system"
import { LLM } from "../core/llm"
import { Bus } from "../../bus"
import { Service as ResolveToolsService, defaultLayer as resolveToolsLayer } from "../core/resolve-tools"
import { Service as UserPartService, defaultLayer as userPartLayer } from "../core/user-part"
import { Service as SubtaskService, defaultLayer as subtaskLayer } from "../core/subtask"
import { Service as ShellService, defaultLayer as shellLayer } from "../core/shell-exec"
import { type PromptInput, type ShellInput, type CommandInput, LoopInput } from "./prompt-schemas"
import { type Interface, Service } from "./prompt-types"
import { resolvePromptParts } from "./prompt-resolve"
import { getModel, lastModel, lastAssistant } from "./prompt-model"
import { createUserMessage } from "./prompt-user-msg"
import { ensureTitle } from "./prompt-title"
import { insertReminders } from "./prompt-reminders"
import { commandHandler } from "./prompt-command"
import { createRunLoop } from "./prompt-loop"

export const shouldAskPlanFollowup = SaeeolSessionPrompt.shouldAskPlanFollowup

export const layer = Layer.effect(
  Service,
  Effect.gen(function* () {
    const bus = yield* Bus.Service
    const status = yield* SessionStatus.Service
    const sessions = yield* SessionMod.Service
    const agents = yield* Agent.Service
    const provider = yield* Provider.Service
    const processor = yield* SessionProcessor.Service
    const compaction = yield* SessionCompaction.Service
    const plugin = yield* Plugin.Service
    const commands = yield* Command.Service
    const config = yield* Config.Service
    const permission = yield* Permission.Service
    const fsys = yield* AppFileSystem.Service
    const mcp = yield* MCP.Service
    const lsp = yield* LSP.Service
    const registry = yield* ToolRegistry.Service
    const truncate = yield* Truncate.Service
    const spawner = yield* ChildProcessSpawner.ChildProcessSpawner
    const scope = yield* Scope.Scope
    const instruction = yield* Instruction.Service
    const state = yield* SessionRunState.Service
    const revert = yield* SessionRevert.Service
    const summary = yield* SessionSummary.Service
    const sys = yield* SystemPrompt.Service
    const llm = yield* LLM.Service
    const resolveToolsSvc = yield* ResolveToolsService
    const userPartSvc = yield* UserPartService
    const subtaskSvc = yield* SubtaskService
    const shellSvc = yield* ShellService

    const deps = {
      bus, status, sessions, agents, provider, processor, compaction, plugin,
      commands, config, permission, fsys, mcp, lsp, registry, truncate,
      spawner, scope, instruction, state, revert, summary, sys, llm,
      resolveToolsSvc, userPartSvc, subtaskSvc, shellSvc,
    }

    const resolvePromptPartsFn = resolvePromptParts(deps as any)
    const getModelFn = getModel(deps as any)
    const lastModelFn = lastModel(deps as any)
    const lastAssistantFn = lastAssistant(deps as any)
    const createUserMessageFn = createUserMessage(deps as any)
    const ensureTitleFn = ensureTitle(deps as any)
    const insertRemindersFn = insertReminders(deps as any, SaeeolSessionPrompt)
    const commandFn = commandHandler(deps as any, SaeeolSessionProcessor.markReviewTelemetry.bind(SaeeolSessionProcessor))

    const cancel = Effect.fn("SessionPrompt.cancel")(function* (sessionID: SessionID) {
      yield* SaeeolSessionPromptQueue.cancel(sessionID)
      SaeeolSessionPrompt.abortPlanFollowup(sessionID)
      yield* state.cancel(sessionID)
    })

    const { runLoop, loop, shell } = createRunLoop(
      deps as any,
      {
        getModel: getModelFn,
        lastModel: lastModelFn,
        lastAssistant: lastAssistantFn,
        createUserMessage: createUserMessageFn,
        ensureTitle: ensureTitleFn,
        insertReminders: insertRemindersFn,
        resolvePromptParts: resolvePromptPartsFn,
        commandHandler: commandFn,
        SaeeolSessionPrompt,
        SaeeolSessionPromptQueue,
        SaeeolSession,
        SaeeolSessionProcessor,
      } as any,
    )

    let promptFn: any

    const promptImpl = Effect.fn("SessionPrompt.prompt")(function* (input: PromptInput) {
      return yield* promptFn(input)
    })

    promptFn = Effect.fn("SessionPrompt.prompt")(function* (input: PromptInput) {
      const session = yield* sessions.get(input.sessionID)
      yield* revert.cleanup(session)
      yield* SaeeolSessionPrompt.recoverDanglingAssistant({ sessionID: input.sessionID, status, sessions })
      yield* SaeeolSessionPrompt.recoverProviderFinishError({ sessionID: input.sessionID, status, sessions })
      const message = yield* createUserMessageFn(input)
      yield* sessions.touch(input.sessionID)
      const permissions: Permission.Ruleset = []
      for (const [t, enabled] of Object.entries(input.tools ?? {})) {
        permissions.push({ permission: t, action: enabled ? "allow" : "deny", pattern: "*" })
      }
      if (permissions.length > 0) {
        session.permission = permissions
        yield* sessions.setPermission({ sessionID: session.id, permission: permissions })
      }
      yield* Effect.promise(() => Suggestion.dismissAll(input.sessionID))
      yield* Effect.promise(() => Question.dismissAll(input.sessionID))
      if (input.noReply === true) return message
      return yield* (SaeeolSessionPromptQueue.enqueue as any)(
        input.sessionID, message.info.id,
        loop({ sessionID: input.sessionID }),
        lastAssistantFn(input.sessionID),
      )
    })

    return Service.of({
      cancel,
      prompt: promptFn,
      loop: loop as any,
      shell: shell as any,
      command: commandFn as any,
      resolvePromptParts: resolvePromptPartsFn as any,
    })
  }),
)

export const defaultLayer = Layer.suspend(() =>
  layer.pipe(
    Layer.provide(
      Layer.mergeAll(
        resolveToolsLayer, userPartLayer, subtaskLayer, shellLayer,
        SessionRunState.defaultLayer, SessionStatus.defaultLayer,
        SessionCompaction.defaultLayer, SessionProcessor.defaultLayer,
        Command.defaultLayer, Permission.defaultLayer, MCP.defaultLayer,
        LSP.defaultLayer, ToolRegistry.defaultLayer, Truncate.defaultLayer,
        Provider.defaultLayer, Config.defaultLayer, Instruction.defaultLayer,
        AppFileSystem.defaultLayer, Plugin.defaultLayer, SessionMod.defaultLayer,
        SessionRevert.defaultLayer, SessionSummary.defaultLayer, Agent.defaultLayer,
        SystemPrompt.defaultLayer, LLM.defaultLayer, Bus.layer,
        CrossSpawnSpawner.defaultLayer,
      ),
    ),
  ),
)

export { Service } from "./prompt-types"
export type { Interface } from "./prompt-types"

export {
  PromptInput,
  type PromptInput as PromptInputType,
  ShellInput,
  type ShellInput as ShellInputType,
  CommandInput,
  type CommandInput as CommandInputType,
  LoopInput,
  createStructuredOutputTool,
  STRUCTURED_OUTPUT_DESCRIPTION, STRUCTURED_OUTPUT_SYSTEM_PROMPT,
  REQUEST_PRUNE_BYTES,
  bashRegex, argsRegex, placeholderRegex, quoteTrimRegex,
} from "./prompt-schemas"

const { runPromise } = makeRuntime(Service, defaultLayer)
export const prompt = (input: PromptInput) => runPromise((svc) => svc.prompt(input))
export const loopExport = (input: LoopInput) => runPromise((svc) => svc.loop(input))
export const cancel = (sessionID: SessionID) => runPromise((svc) => svc.cancel(sessionID))

export * as SessionPrompt from "./prompt"