import type {
  GoatChainContext as DimSdkContext,
  ToolCall,
  ToolResult,
} from '#cli/core/model'
import type { CliAgentBridge as DimSdkAgent } from '../../agent-bridge'
import { debugLog } from '#cli/core/debug-log'
import { rewriteOversizedInputToReference } from '#cli/core/long-input-cache'
import {
  sleep,
  yieldToEventLoop,
} from '#cli/core/store/async'
import {
  createEnvReader,
  resolveStreamTimeouts,
} from '#cli/core/store/env'
import { id } from '#cli/core/store/ids'
import { createSendInterrupt } from '#cli/core/store/send-interrupt'
import { createSendLoop } from '#cli/core/store/send-loop'
import { createSendQueue } from '#cli/core/store/send-queue'
import { createSessionUsagePersistence } from '#cli/core/store/session-usage'
import {
  buildLegacyConversation,
  createLegacyMockStreamRunner,
} from '#cli/core/store/stream-runner'
import { createSessionRequestBudgetPersistence } from './request-budget'
import { createConversationImageResolver } from './resolve-conversation-images'
import { createToolExecution } from '#cli/core/store/tool-execution'
import { normalizeThemeMode } from '#cli/core/theme'
import { normalizeText } from '#cli/core/utils'
import { setLocale } from '#cli/i18n'
import { computed } from 'vue'
import { createApprovalsFlow } from './approvals-flow'
import { createContextActions } from './context-actions'
import { scheduleToolCallDiffHighlightByCallId as scheduleToolCallDiffHighlightByCallIdImpl } from './diff-highlight'
import { scheduleAssistantMarkdownCodeHighlight as scheduleAssistantMarkdownCodeHighlightImpl } from './markdown-code-highlight'
import { createMessagePipeline } from './message-pipeline'
import { createNativeStreamRunner } from './native-stream-runner'
import { scheduleToolCallReadHighlightByCallId as scheduleToolCallReadHighlightByCallIdImpl } from './read-highlight'
import { createSessionUi } from './session-ui'
import { createDimSdkState } from './state'
import { createToolUiActions } from './tool-ui'
import { createUiState } from './ui-state'
import { createUiSync } from './ui-sync'

export type ToolRunner = (call: ToolCall) => Promise<ToolResult>

export type UserInput = Readonly<{
  content: string
  focusFiles?: readonly string[]
  steer?: boolean
}>

export type DimSdkStore = ReturnType<typeof createDimSdkStore>

export function createDimSdkStore(
  opts: Readonly<{
    toolRunner: ToolRunner
    context: DimSdkContext
    agent: DimSdkAgent
  }>,
) {
  // Compose the store from focused submodules to keep the main loop readable.
  const externalAgentProvided = Boolean(opts.agent)
  const { state, initialLocale } = createDimSdkState({
    context: opts.context,
    agent: opts.agent,
  })

  const agent: DimSdkAgent = opts.agent

  const usagePct = computed(() => {
    const budget = Math.max(1, state.usage.budget)
    return Math.min(1, state.usage.total_tokens / budget)
  })

  const { applyUsageFromSession } = createSessionUsagePersistence({ state })
  const { applyLastRequestBudget } = createSessionRequestBudgetPersistence({
    state,
  })
  const { setFocusFiles, setCwd, setEnvVar, setAgent, setThemeMode }
    = createContextActions(state)

  const messagePipeline = createMessagePipeline({
    state,
    debugLog,
  })
  const {
    pushMessage,
    setSessionHistoryDirty,
    replaceMessage,
    updateAssistant,
    setAssistantStatus,
    setAssistantThinking,
    startNewThinkingBlock,
    endThinkingBlock,
    appendAssistantMarkdown,
    pushAssistantMarkdownPart,
    appendInfoPart,
    upsertSubagentInfo,
    tickSubagentFrame,
    appendAssistantLlmMeta,
    updateAssistantLlmMeta,
    upsertToolCallDelta,
    upsertToolCallEndWithHighlights,
    upsertToolOutputStart,
    appendToolOutputDelta,
    attachToolResultPart,
    insertApprovalPart,
    toggleChecklist,
    toggleBlockCollapsed,
    replaceMessages,
    ensureErrorVisibleInTranscript,
    flushPendingAssistantOutput,
    scheduleToolResultHighlight,
    hasRevertedMessages,
    revertedMessageCount,
    revertFromMessage,
    revertFilesFromMessage,
    restoreRevertedMessages,
    clearRevertedMessages,
    upsertChecklistPartFromDimSdk,
  } = messagePipeline

  // Tool UI interactions (selection, expansion, highlighting) are isolated here.
  const toolUiActions = createToolUiActions({
    state,
    updateAssistant,
    scheduleToolCallReadHighlightByCallId:
      scheduleToolCallReadHighlightByCallIdImpl,
    scheduleToolCallDiffHighlightByCallId:
      scheduleToolCallDiffHighlightByCallIdImpl,
  })
  const {
    selectToolCall,
    toggleBashOutput,
    loadMoreBashOutput,
    toggleEditDiffLayout,
    toggleToolCallCollapsed,
    toggleToolCallGroupCollapsed,
    toggleToolCallGroup,
    clearCollapsedToolCalls,
    setAllThinkingCollapsed,
  } = toolUiActions

  const sendQueue = createSendQueue()
  const { enqueue: enqueueSendLoop, serializeReceive } = sendQueue
  const sendLoopState = { inSendQueueRun: false }
  const isInSendQueueRun = () => sendLoopState.inSendQueueRun

  const streamTimeouts = resolveStreamTimeouts(
    createEnvReader((state.context.env ?? {}) as Record<string, string | undefined>),
  )
  const hasNativeSessionBridge
    = Boolean(agent.sessions)
      || typeof agent.ensureSession === 'function'
      || typeof agent.openSession === 'function'
  const hasLegacyMockAgent = typeof agent.receiveMessage === 'function'

  const streamRunner
    = !hasNativeSessionBridge && hasLegacyMockAgent
      ? createLegacyMockStreamRunner({
          state,
          agent: agent as any,
          id,
          buildAcpConversation: buildLegacyConversation,
          resolveConversationImages: createConversationImageResolver(state),
          streamIdleTimeoutMs: streamTimeouts.idleMs,
          streamTotalTimeoutMs: streamTimeouts.totalMs,
          pushMessage,
          updateAssistant,
          setAssistantStatus,
          setAssistantThinking,
          startNewThinkingBlock,
          endThinkingBlock,
          appendAssistantMarkdown,
          pushAssistantMarkdownPart,
          appendInfoPart,
          appendAssistantLlmMeta,
          updateAssistantLlmMeta,
          upsertToolCallDelta,
          upsertToolCallEnd: upsertToolCallEndWithHighlights,
          upsertToolOutputStart,
          appendToolOutputDelta,
          flushPendingAssistantOutput,
          insertApprovalPart,
          attachToolResultPart,
          scheduleToolResultHighlight,
          upsertChecklistPartFromGoatChain: upsertChecklistPartFromDimSdk,
          applyLastRequestBudget,
          applyUsageFromSession,
          ensureErrorVisibleInTranscript,
        })
      : createNativeStreamRunner({
          state,
          agent,
          id,
          pushMessage,
          updateAssistant,
          setAssistantStatus,
          setAssistantThinking,
          startNewThinkingBlock,
          endThinkingBlock,
          appendAssistantMarkdown,
          pushAssistantMarkdownPart,
          appendInfoPart,
          upsertSubagentInfo,
          tickSubagentFrame,
          upsertToolCallDelta,
          upsertToolCallEnd: upsertToolCallEndWithHighlights,
          upsertToolOutputStart,
          appendToolOutputDelta,
          insertApprovalPart,
          attachToolResultPart,
          scheduleToolResultHighlight,
          upsertChecklistPartFromDimSdk,
          flushPendingAssistantOutput,
          applyLastRequestBudget,
          applyUsageFromSession,
          ensureErrorVisibleInTranscript,
        })
  const {
    runAgentRound,
    cancelActiveAgentStream,
    cancelActiveAgentStreamAndWaitWithTimeout,
    abortActiveAgentRound,
  } = streamRunner

  const toolExecution = createToolExecution({
    state,
    externalAgentProvided,
    toolRunner: opts.toolRunner,
    id,
    pushMessage,
    updateAssistant,
    setAssistantStatus,
    insertApprovalPart,
    attachToolResultPart,
    runAgentRound: o => runAgentRound(o as any),
    sleep,
    serializeReceive,
    isInSendQueueRun,
  })
  const { ensureApproved, findToolCall, resolveActiveApproval, runToolCall }
    = toolExecution

  const sendInterrupt = createSendInterrupt({
    state,
    abortActiveAgentRound,
    cancelActiveAgentStream,
    cancelActiveAgentStreamAndWaitWithTimeout,
    setSessionHistoryDirty,
    setAssistantStatus,
    findToolCall,
    runAgentRound,
    serializeReceive,
    interruptSession: async (sessionId: string) => {
      const interruptSession = agent.sessions?.interrupt
      if (!sessionId || typeof interruptSession !== 'function')
        return
      try {
        await interruptSession(sessionId)
      }
      catch (error) {
        debugLog(
          'agent_interrupt_session_error',
          error instanceof Error
            ? {
                session_id: sessionId,
                message: error.message,
                stack: error.stack,
              }
            : { session_id: sessionId, error: String(error) },
        )
      }
    },
  })
  const {
    interrupt,
    interruptAndWait,
    submitAskUserAnswers,
    cancelAskUser,
    getEpoch,
  } = sendInterrupt

  const sendLoop = createSendLoop({
    state,
    externalAgentProvided,
    id,
    pushMessage,
    replaceMessage,
    setAssistantStatus,
    setFocusFiles,
    runAgentRound,
    abortActiveAgentRound,
    cancelActiveAgentStream,
    cancelActiveAgentStreamAndWaitWithTimeout,
    ensureApproved,
    runToolCall,
    resolveActiveApproval,
    sleep,
    yieldToEventLoop,
    enqueue: enqueueSendLoop,
    setInSendQueueRun: (value) => {
      sendLoopState.inSendQueueRun = value
    },
    interruptAndWait,
    getInterruptEpoch: getEpoch,
    rewriteUserInput: async ({ content, sessionId }) => {
      const result = await rewriteOversizedInputToReference({
        content,
        sessionId,
        source: 'chat_input',
      })
      return result.content
    },
  })
  const { send } = sendLoop

  const steer = async (input: string): Promise<boolean> => {
    const content = normalizeText(input)
    if (!content)
      return false

    if (state.runningAgentRound) {
      const sessionId = String(state.sessionId ?? '').trim()
      if (!sessionId || typeof agent.steerSession !== 'function') {
        return false
      }

      try {
        return await agent.steerSession(sessionId, content)
      }
      catch (error) {
        debugLog(
          'agent_steer_error',
          error instanceof Error
            ? {
                session_id: sessionId,
                message: error.message,
                stack: error.stack,
              }
            : {
                session_id: sessionId,
                error: String(error),
              },
        )
        return false
      }
    }

    await send({ content, steer: true })
    return true
  }

  createApprovalsFlow({
    state,
    resolveActiveApproval,
    isInSendQueueRun,
  })

  createUiSync({
    state,
    initialLocale,
    setLocale: locale => setLocale(locale as any),
    normalizeThemeMode,
    scheduleToolCallReadHighlightByCallId:
      scheduleToolCallReadHighlightByCallIdImpl,
    scheduleToolCallDiffHighlightByCallId:
      scheduleToolCallDiffHighlightByCallIdImpl,
    scheduleAssistantMarkdownCodeHighlight:
      scheduleAssistantMarkdownCodeHighlightImpl,
    updateAssistant,
  })

  const { setMode, getMode } = createUiState({
    state,
    agent,
  })

  const {
    compactSession,
    upgradeNotification,
    showUpgradeNotification,
    dismissUpgradeNotification,
  } = createSessionUi({
    state,
    agent,
    applyLastRequestBudget,
  })

  return {
    state,
    agent,
    usagePct,
    scheduleToolResultHighlight,
    selectToolCall,
    toggleBashOutput,
    loadMoreBashOutput,
    toggleEditDiffLayout,
    toggleChecklist,
    toggleBlockCollapsed,
    toggleToolCallCollapsed,
    toggleToolCallGroupCollapsed,
    toggleToolCallGroup,
    clearCollapsedToolCalls,
    setAllThinkingCollapsed,
    send,
    steer,
    sendUser: send,
    sendUserInput: (input: UserInput) => send(input),
    setFocusFiles,
    setCwd,
    setEnvVar,
    setAgent,
    setThemeMode,
    replaceMessages,
    flushPendingAssistantOutput,
    approve: () => resolveActiveApproval('approved'),
    deny: () => resolveActiveApproval('denied'),
    runToolCall,
    interrupt,
    submitAskUserAnswers,
    cancelAskUser,
    // Revert/Redo functionality
    revertFromMessage,
    revertFilesFromMessage,
    restoreRevertedMessages,
    clearRevertedMessages,
    hasRevertedMessages,
    revertedMessageCount,
    // Mode switching
    setMode,
    getMode,
    compactSession,
    // Upgrade notification
    upgradeNotification,
    showUpgradeNotification,
    dismissUpgradeNotification,
    applyUsageFromSession,
    applyLastRequestBudget,
  }
}
