import type { ProviderAdapter, ProviderCustomModel, ProviderState } from '#cli/compat/providers'
import type { DimSdkStore } from '#cli/core/store'
import type { DimcodeUserCacheV1, DimcodeUserConfigV1 } from '../user-cache'
import { normalizeShortcutBindings } from '#cli/core/ui-settings'
import { normalizeNum, normalizeStr, toPlainJson } from '../user-cache'
import { normalizeLimit } from '../utils/normalize'

type PersistedWorkspaceSelection = NonNullable<
  NonNullable<DimcodeUserCacheV1['settings']>['workspaceSelectionByCwd']
>[string]

type PersistedProviderModel = NonNullable<
  NonNullable<
    NonNullable<DimcodeUserConfigV1['settings']>['customProviders']
  >[number]['models']
>[number]

type PersistedProviderConnection = NonNullable<
  NonNullable<DimcodeUserConfigV1['settings']>['providerConnections']
>[string]

type PersistedCustomProvider = NonNullable<
  NonNullable<DimcodeUserConfigV1['settings']>['customProviders']
>[number]

type PersistedLegacyCustomProvider = NonNullable<
  NonNullable<DimcodeUserConfigV1['settings']>['customProvider']
>

function normalizePersistedReasoning(
  reasoning: ProviderCustomModel['reasoning'] | null | undefined,
): PersistedProviderModel['reasoning'] | undefined {
  if (!reasoning?.supported)
    return undefined
  return typeof reasoning.default === 'boolean'
    ? { supported: true, default: reasoning.default }
    : { supported: true }
}

function normalizePersistedProviderModel(
  model: ProviderCustomModel | null | undefined,
): PersistedProviderModel | null {
  const id = normalizeStr(model?.id)
  if (!id)
    return null

  const name = normalizeStr(model?.name)
  const limit = normalizeLimit(model?.limit)
  const vision = typeof model?.vision === 'boolean' ? model.vision : undefined
  const reasoning = normalizePersistedReasoning(model?.reasoning)

  return {
    id,
    ...(name ? { name } : {}),
    ...(limit ? { limit } : {}),
    ...(typeof vision === 'boolean' ? { vision } : {}),
    ...(reasoning ? { reasoning } : {}),
  }
}

function normalizePersistedLegacyProviderModel(
  model: ProviderCustomModel | null | undefined,
): PersistedProviderModel | null {
  const persisted = normalizePersistedProviderModel(model)
  if (!persisted)
    return null
  const { name: _name, ...legacyModel } = persisted
  return legacyModel
}

function normalizePersistedProviderConnection(
  connection: ProviderState['connections'][string] | null | undefined,
): PersistedProviderConnection | null {
  const apiKey = normalizeStr(connection?.apiKey)
  const baseUrl = normalizeStr(connection?.baseUrl)
  const models = (connection?.models ?? [])
    .map(normalizePersistedProviderModel)
    .filter(
      (model): model is PersistedProviderModel =>
        Boolean(model),
    )

  if (!apiKey && !baseUrl && models.length === 0)
    return null

  return {
    ...(apiKey ? { apiKey } : {}),
    ...(baseUrl ? { baseUrl } : {}),
    ...(models.length ? { models } : {}),
  }
}

function normalizePersistedCustomProvider(
  provider: ProviderState['customProviders'][number] | null | undefined,
): PersistedCustomProvider | null {
  const id = normalizeStr(provider?.id)
  const name = normalizeStr(provider?.name)
  const adapter = normalizeStr(provider?.adapter) as ProviderAdapter | undefined
  if (!id || !name || !adapter)
    return null

  const baseUrl = normalizeStr(provider?.baseUrl)
  const apiKey = normalizeStr(provider?.apiKey)
  const models = (provider?.models ?? [])
    .map(normalizePersistedProviderModel)
    .filter(
      (model): model is PersistedProviderModel =>
        Boolean(model),
    )

  return {
    id,
    name,
    adapter,
    ...(baseUrl ? { baseUrl } : {}),
    ...(apiKey ? { apiKey } : {}),
    ...(models.length ? { models } : {}),
  }
}

function normalizePersistedWorkspaceSelection(
  value: { providerId: string, modelId?: string } | null | undefined,
): PersistedWorkspaceSelection | null {
  const providerId = normalizeStr(value?.providerId)
  if (!providerId)
    return null

  const modelId = normalizeStr(value?.modelId)
  return {
    providerId,
    ...(modelId ? { modelId } : {}),
  }
}

/**
 * Build the next persisted UI cache from the current store state.
 *
 * Important: this is *not* session persistence. Sessions are stored by DimCode's
 * SessionManager/StateStore under the resolved state directory.
 */
export function buildUserCacheFromStore(
  opts: Readonly<{
    prev: DimcodeUserCacheV1
    store: DimSdkStore
  }>,
): DimcodeUserCacheV1 {
  const state = opts.store.state
  const ui = state.ui
  const prevSettings = opts.prev.settings ?? {}
  const authMethod = prevSettings.authMethod

  return {
    version: 1,
    updatedAt: Date.now(),
    settings: {
      cwd: normalizeStr(state.context.cwd),
      focusFiles: state.context.focusFiles?.length
        ? [...state.context.focusFiles]
        : undefined,
      model: normalizeStr(state.config.model),
      authMethod,
      contextWindow: normalizeNum(state.config.context_window),
      temperature: normalizeNum(state.config.temperature),
      interleavedThinking: state.config.interleaved_thinking,
      toolApprovals: (() => {
        const m = state.approval.mode
        if (m === 'auto') return 'auto'
        if (m === 'strict') return 'strict'
        return 'normal'
      })(),
      locale: normalizeStr(ui.locale),
      themeMode: normalizeStr(ui.theme.mode),
      onboardingSeenVersion: normalizeStr(
        ui.onboarding?.lastSeenVersion,
      ),
      statusCompact: ui.statusCompact,
      shortcuts: normalizeShortcutBindings(ui.shortcuts),
      greetingLastSeenDate: normalizeStr(
        ui.greeting?.lastSeenDate,
      ),
      providerFunnelCounts: (() => {
        const entries = Object.entries(ui.providerFunnel.counts)
          .map(([step, count]) => [String(step), Number(count)] as const)
          .filter(
            ([step, count]) => step && Number.isFinite(count) && count > 0,
          )
        return entries.length ? Object.fromEntries(entries) : undefined
      })(),
      workspaceSelectionByCwd: (() => {
        const entries = Object.entries(ui.workspaceSelectionByCwd)
          .map(([cwd, value]) => {
            const key = normalizeStr(cwd)
            const selection = normalizePersistedWorkspaceSelection(value)
            if (!key || !selection) return null
            return [key, selection] as const
          })
          .filter(
            (
              entry,
            ): entry is readonly [
              string,
              PersistedWorkspaceSelection,
            ] => Boolean(entry),
          )
        return entries.length ? Object.fromEntries(entries) : undefined
      })(),
    },
    lastSessionId: normalizeStr(state.sessionId) || opts.prev.lastSessionId,
  }
}

export function buildUserConfigFromStore(
  opts: Readonly<{
    prev: DimcodeUserConfigV1
    store: DimSdkStore
  }>,
): DimcodeUserConfigV1 {
  const providerState = opts.store.state.provider
  const providerId = normalizeStr(providerState.activeId)
  const providerConnections = (() => {
    const entries = Object.entries(providerState.connections)
      .map(([id, conn]) => {
        const key = normalizeStr(id)
        const next = normalizePersistedProviderConnection(conn)
        if (!key || !next) return null
        return [key, next] as const
      })
      .filter(
        (
          entry,
        ): entry is readonly [string, PersistedProviderConnection] =>
          Boolean(entry),
      )
    return Object.fromEntries(entries)
  })()
  const activeModelByProvider = Object.fromEntries(
    Object.entries(providerState.activeModelByProvider)
      .map(([id, modelId]) => {
        const providerId = normalizeStr(id)
        const nextModelId = normalizeStr(modelId)
        if (!providerId || !nextModelId) return null
        return [providerId, nextModelId] as const
      })
      .filter(
        (
          entry,
        ): entry is readonly [string, string] => Boolean(entry),
      ),
  )
  const customProviders = providerState.customProviders
    .map(normalizePersistedCustomProvider)
    .filter(
      (provider): provider is PersistedCustomProvider =>
        Boolean(provider),
    )

  const legacyCustomProvider = (() => {
    // When customProviders array already has entries, do not duplicate into
    // the legacy customProvider field – that causes two identical configs to
    // appear after a rename (one from customProviders, one from legacy).
    if (customProviders.length > 0) return undefined

    const legacy = providerState.custom ?? {}
    const legacyModels = legacy.models
      .map(normalizePersistedLegacyProviderModel)
      .filter(
        (model): model is PersistedProviderModel =>
          Boolean(model),
      )
    const baseUrl = normalizeStr(legacy.baseUrl)
    const apiKey = normalizeStr(legacy.apiKey)
    if (!baseUrl && !apiKey && !legacyModels?.length) return undefined
    const next: PersistedLegacyCustomProvider = {
      baseUrl,
      apiKey,
      models: legacyModels?.length ? toPlainJson(legacyModels) : undefined,
    }
    return next
  })()

  const hasSettings = Boolean(
    providerId ||
    Object.keys(activeModelByProvider).length ||
    Object.keys(providerConnections).length ||
    customProviders.length ||
    legacyCustomProvider,
  )

  return {
    version: 1,
    updatedAt: Date.now(),
    settings: hasSettings
      ? {
          providerId,
          activeModelByProvider: Object.keys(activeModelByProvider).length
            ? toPlainJson(activeModelByProvider)
            : undefined,
          providerConnections: Object.keys(providerConnections).length
            ? toPlainJson(providerConnections)
            : undefined,
          customProviders: customProviders.length
            ? toPlainJson(customProviders)
            : undefined,
          customProvider: legacyCustomProvider,
        }
      : opts.prev.settings,
  }
}
