import type { McpServerSettings } from '#cli/core/config'
import type {
  ApprovalRequest,
  GoatChainContext as DimSdkContext,
  Message,
  RunConfig,
  TokenUsage,
} from '#cli/core/model'
import type {
  SessionQueueErrorCode,
  SessionQueueItem,
  SessionQueueStatus,
} from '#cli/core/session-meta'
import type { FileRevert } from '#cli/core/store/file-reverts'
import type { PausedState } from '#cli/core/store/types'
import type { ThemeId } from '#cli/core/theme'
import type {
  ProviderCustomConfig,
  ProviderState,
} from '#cli/compat/providers'
import { defaultSessionUiMetadata } from '#cli/core/session-meta'
import {
  createEnvReader,
  resolveInitialApprovalMode,
  resolveInitialRunConfig,
  resolveInitialSessionId,
} from '#cli/core/store/env'
import {
  resolveInitialThemeId,
} from '#cli/core/theme'
import {
  DEFAULT_SHORTCUTS,
  DEFAULT_TIP_INTENSITY,
} from '#cli/core/ui-settings'
import { resolveLocalePreference } from '#cli/i18n'
import { reactive } from 'vue'
import type { CliAgentBridge as DimSdkAgent } from '../../agent-bridge'

type PendingApproval = Readonly<{
  request: ApprovalRequest
  assistantId: string
}>

export function createDimSdkState(
  opts: Readonly<{ context: DimSdkContext; agent: DimSdkAgent }>,
) {
  const initialEnv = opts.context?.env
  const env = createEnvReader(initialEnv)
  const initialApprovalMode = resolveInitialApprovalMode(env)
  const initialRunConfig = resolveInitialRunConfig(env)
  const initialMode =
    typeof opts.agent?.getMode === 'function' ? opts.agent.getMode() : 'agent'
  const initialLocale = resolveLocalePreference({
    explicit: env.prefixedStr('LANG'),
    env: initialEnv,
  })
  const initialThemeMode = resolveInitialThemeId(initialEnv)

  const state = reactive({
    config: {
      ...initialRunConfig,
    } satisfies RunConfig,
    usage: {
      prompt_tokens: 0,
      completion_tokens: 0,
      total_tokens: 0,
      budget: initialRunConfig.context_window,
    } satisfies TokenUsage,
    lastRequestBudget: null as null | {
      runId?: string
      requestId?: string
      autoBudgetOutcome: 'budget-unknown' | 'compacted' | 'failed-to-fit' | 'not-needed'
      autoBudgetReasonCode?: string
      budgetSource?: string
      contextWindowTokens?: number
      estimatedInputTokens: number
      inputBudgetTokens?: number
      maxOutputTokens?: number
      protocolReserveTokens?: number
      reservedOutputTokens?: number
      thresholdTokens?: number
      maxInputTokens?: number
      triggerRatio?: number
      usedAutoCompaction: boolean
      updatedAt?: string
      updatedAtMs: number
    },
    runningAgentRound: false,
    thinking: {
      active: false,
      assistantId: null as string | null,
      partId: null as string | null,
    },
    ui: {
      mode: initialMode as 'agent' | 'plan',
      locale: initialLocale,
      theme: {
        mode: initialThemeMode as ThemeId,
      },
      statusCompact: false,
      onboarding: {
        lastSeenVersion: '',
      },
      providerFunnel: {
        counts: {} as Record<string, number>,
        events: [] as Array<{
          step: string
          at: number
          providerId?: string
          result?: string
        }>,
      },
      workspaceSelectionByCwd: {} as Record<
        string,
        { providerId: string; modelId?: string }
      >,
      tipIntensity: DEFAULT_TIP_INTENSITY,
      shortcuts: {
        ...DEFAULT_SHORTCUTS,
      },
      recentCommands: [] as string[],
      greeting: {
        lastSeenDate: '',
      },
      delight: {
        compactStreak: 0,
        lastCompactAtMs: 0,
        lastDelightAtMs: 0,
      },
      expandToolCallsAndThinking: false,
      upgradeNotification: {
        visible: false,
        message: '',
      },
      debug: {
        lastRoundStartedAtMs: 0,
        lastRoundFirstEventAtMs: 0,
        lastRoundDoneAtMs: 0,
        lastRoundHadError: false,
      },
      tool: {
        callId: null as string | null,
        name: '',
        startedAtMs: 0,
      },
    },
    context: opts.context,
    messages: [] as Message[],
    // True when local transcript has diverged from persisted session history (e.g. revert).
    // The next non-resume send should force-sync agent session history from ACP messages.
    sessionHistoryDirty: false,
    revertedMessages: [] as Message[], // Messages that have been reverted (for redo)
    revertedFilesApplied: false,
    fileRevertsByToolCallId: new Map<string, FileRevert>(),
    // Derived user-input history used for input navigation. Keep this out of computed() so
    // assistant streaming updates don't force a full scan of `messages` on every chunk.
    userHistory: [] as string[],
    // Hints for the incremental layout builder so non-tail message updates render immediately.
    // This is especially important for async updates like Read syntax-highlighting previews.
    layoutDirtyMessageId: null as string | null,
    layoutDirtySeq: 0,
    chromeVersion: 0,
    selectedToolCallId: null as string | null,
    runningToolCallId: null as string | null,
    expandedBashOutputs: new Set<string>() as Set<string>, // toolCallId -> expanded state
    expandedToolOutputLineCounts: new Map<string, number>() as Map<
      string,
      number
    >, // toolCallId -> expanded visible line budget
    collapsedToolCallIds: new Set<string>() as Set<string>, // toolCallId -> collapsed state when expanding all
    expandedToolCallGroups: new Set<string>() as Set<string>, // groupKey -> expanded state for tool call groups in manual mode
    unifiedEditDiffToolCallIds: new Set<string>() as Set<string>, // toolCallId -> force unified (single-column) diff layout for Edit tool
    // `sessionId` is assigned by `agent.createSession()` (or `agent.resumeSession()`).
    // The UI should treat it as opaque and never generate one locally.
    sessionId: resolveInitialSessionId(env),
    sessionMeta: {
      ...defaultSessionUiMetadata(),
      queue: [] as SessionQueueItem[],
      status: 'idle' as SessionQueueStatus,
      lastErrorCode: undefined as SessionQueueErrorCode | undefined,
    },
    paused: null as PausedState | null,
    approval: {
      active: null as ApprovalRequest | null,
      approvedToolCalls: {} as Record<string, true | undefined>,
      pendingAutoRun: null as null | {
        assistantId: string
        toolCallId: string
      },
      mode: initialApprovalMode,
      queue: [] as PendingApproval[],
      decisions: {} as Record<
        string,
        Readonly<{ approved: boolean; reason?: string }>
      >,
    },
    provider: {
      activeId: '',
      activeModelByProvider: {} as Record<string, string>,
      connections: {} as Record<string, { apiKey?: string; baseUrl?: string }>,
      customProviders: [] as ProviderState['customProviders'],
      custom: {
        baseUrl: undefined,
        apiKey: undefined,
        models: [] as Array<{ id: string; name?: string }>,
      } as ProviderCustomConfig,
    } satisfies ProviderState,
    tools: {
      websearch: {
        apiKey: '',
        apiEndpoint: 'https://google.serper.dev/search?format=json',
        numResults: 10,
      },
    },
    mcp: {
      servers: [] as McpServerSettings[],
    },
  })

  return { state, env, initialLocale }
}

export type StoreState = ReturnType<typeof createDimSdkState>['state']
