import type { DimcodeUserConfigV1 } from '#cli/compat/config'
import type { McpServerConfig } from '#cli/compat/mcp'
import type { ProviderModel } from '#cli/compat/providers'
import type { McpServerSettings } from '../core/config'
import { homedir } from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { getBuiltinProvider, normalizeProviderId } from '#cli/compat/providers'
import {
  APP_DIR_NAME,
  CONFIG_DIR_NAME,
  STATE_DIR_NAME,
} from '../core/config'
import {
  normalizeStoragePath,
  resolveConfigHomeFromEnv,
} from '../core/storage-paths'

type UserConfigSettings = NonNullable<DimcodeUserConfigV1['settings']>
type ProviderConnections = NonNullable<UserConfigSettings['providerConnections']>
type LegacyCustomProviderConfig = NonNullable<UserConfigSettings['customProvider']>
type ImageCapableModel = Readonly<
  Pick<ProviderModel, 'id' | 'name' | 'display_name' | 'vision' | 'modalities'>
>

function matchesModelId(model: ImageCapableModel, modelId: string): boolean {
  return String(model.id ?? '').trim() === modelId
    || String(model.name ?? '').trim() === modelId
}

export function detectImageCapability(
  config: DimcodeUserConfigV1,
  env: Record<string, string | undefined>,
): boolean {
  const settings = config.settings ?? {}
  const providerId = normalizeProviderId(settings.providerId)
  const activeModelByProvider: NonNullable<UserConfigSettings['activeModelByProvider']>
    = settings.activeModelByProvider ?? {}
  const providerConnections: ProviderConnections = settings.providerConnections ?? {}
  const custom: LegacyCustomProviderConfig = settings.customProvider ?? {}
  const envModel = String(env.DIMCODE_MODEL ?? '').trim()

  const modelId
    = envModel
      || (providerId === 'custom'
        ? String(
            activeModelByProvider.custom ?? custom.models?.[0]?.id ?? '',
          ).trim()
        : String(activeModelByProvider[providerId ?? ''] ?? '').trim())

  if (!providerId || providerId === 'custom' || !modelId)
    return false

  const provider = getBuiltinProvider(providerId)
  const connectionModels = provider?.customModels
    ? providerConnections?.[providerId]?.models
    : undefined
  const models: readonly ImageCapableModel[]
    = Array.isArray(connectionModels) && connectionModels.length
      ? connectionModels
      : provider?.models ?? []
  const model = models.find(candidate => matchesModelId(candidate, modelId))
  if (!model)
    return false

  if (typeof model.vision === 'boolean')
    return model.vision

  const inputs = Array.isArray(model.modalities?.input)
    ? model.modalities.input
    : []
  return inputs.map(s => String(s).toLowerCase()).includes('image')
}

function isPathLike(value: string | undefined): boolean {
  if (!value)
    return false
  return value.includes(path.sep)
}

function normalizePath(value: unknown): string | undefined {
  return normalizeStoragePath(value)
}

function inferConfigHomeFromCacheDir(cacheDir: string): string | undefined {
  const resolved = path.resolve(cacheDir)
  if (path.basename(resolved) !== APP_DIR_NAME)
    return undefined
  const parent = path.dirname(resolved)
  if (path.basename(parent) !== CONFIG_DIR_NAME)
    return undefined
  return parent
}

function inferConfigHomeFromStateDir(stateDir: string): string | undefined {
  const resolved = path.resolve(stateDir)
  if (path.basename(resolved) !== STATE_DIR_NAME)
    return undefined
  const parent = path.dirname(resolved)
  if (path.basename(parent) === CONFIG_DIR_NAME)
    return parent
  if (path.basename(parent) === APP_DIR_NAME) {
    const grandParent = path.dirname(parent)
    if (path.basename(grandParent) === CONFIG_DIR_NAME)
      return grandParent
  }
  return undefined
}

function shouldPreferArgvPath(argvPath: string | undefined): boolean {
  if (!argvPath)
    return false
  const base = path.basename(argvPath)
  if (base === 'dim' || base === 'dimcode') {
    if (argvPath.includes(`${path.sep}packages${path.sep}cli${path.sep}`))
      return true
    if (argvPath.includes(`${path.sep}.bin${path.sep}`))
      return false
    return false
  }
  if (/\.(?:mjs|cjs|js|ts)$/i.test(base))
    return true
  if (argvPath.includes(`${path.sep}.bin${path.sep}`))
    return false
  if (argvPath.includes(`${path.sep}packages${path.sep}cli${path.sep}`))
    return true
  return false
}

export function resolveCliRoot(
  opts: Readonly<{
    argvPath?: string | undefined
    execPath?: string | undefined
    importMetaUrl: string
  }>,
): string {
  const importBase = path.dirname(fileURLToPath(opts.importMetaUrl))
  const argvCandidate = isPathLike(opts.argvPath) ? opts.argvPath : undefined
  const execCandidate = isPathLike(opts.execPath) ? opts.execPath : undefined
  if (!argvCandidate && execCandidate)
    return path.resolve(path.dirname(execCandidate))

  const argvPath = argvCandidate ?? execCandidate
  const argvBase = argvPath ? path.dirname(argvPath) : undefined
  const preferArgv = shouldPreferArgvPath(argvPath)

  if (preferArgv && argvBase && argvPath) {
    const isScript = /\.(?:mjs|cjs|js|ts)$/i.test(path.basename(argvPath))
    return path.resolve(argvBase, isScript ? '..' : '.')
  }

  return path.resolve(importBase, '..')
}

export function resolveStateDir(
  cacheDir: string,
  env?: Record<string, string | undefined>,
): string {
  const explicit = normalizePath(env?.DIMCODE_STATE_DIR)
  if (explicit)
    return explicit
  const configHome
    = resolveConfigHomeFromEnv(env) ?? inferConfigHomeFromCacheDir(cacheDir)
  if (configHome)
    return path.resolve(configHome, STATE_DIR_NAME)
  return path.resolve(cacheDir, STATE_DIR_NAME)
}

export function resolveLegacyStateRoots(
  opts: Readonly<{
    activeStateDir: string
    env?: Record<string, string | undefined>
  }>,
): string[] {
  const configHome
    = resolveConfigHomeFromEnv(opts.env)
      ?? inferConfigHomeFromStateDir(opts.activeStateDir)
  const xdgConfigHome = normalizePath(opts.env?.XDG_CONFIG_HOME)
  const userHome
    = configHome
      ? normalizePath(opts.env?.HOME) ?? normalizePath(homedir())
      : undefined
  const roots = [
    normalizePath(opts.activeStateDir),
    configHome ? path.resolve(configHome, STATE_DIR_NAME) : undefined,
    configHome
      ? path.resolve(configHome, APP_DIR_NAME, STATE_DIR_NAME)
      : undefined,
    configHome ? path.resolve(configHome, 'goatchain', STATE_DIR_NAME) : undefined,
    xdgConfigHome
      ? path.resolve(xdgConfigHome, 'goatchain', 'goatchain', STATE_DIR_NAME)
      : undefined,
    userHome
      ? path.resolve(userHome, '.goatchain', 'goatchain', STATE_DIR_NAME)
      : undefined,
  ].filter((value): value is string => Boolean(value))

  const seen = new Set<string>()
  const unique: string[] = []
  for (const root of roots) {
    if (seen.has(root))
      continue
    seen.add(root)
    unique.push(root)
  }
  return unique
}

export function resolveUploadDir(stateDir: string): string {
  return path.join(stateDir, 'uploads')
}

export function applyStateDirEnv(
  opts: Readonly<{
    env: Record<string, string | undefined>
    contextEnv: Record<string, string | undefined>
    stateDir: string
  }>,
): void {
  opts.env.DIMCODE_STATE_DIR = opts.stateDir
  opts.contextEnv.DIMCODE_STATE_DIR = opts.stateDir
}

export function toMcpServerConfigs(
  settings: readonly McpServerSettings[] | undefined,
): McpServerConfig[] {
  if (!Array.isArray(settings))
    return []

  return settings.map((server) => {
    if (server.transport === 'http' || server.transport === 'sse') {
      return {
        id: server.id,
        name: server.name,
        transport: server.transport,
        url: server.url,
        auth: server.auth?.token
          ? { type: 'bearer', token: server.auth.token }
          : undefined,
        headers: server.headers,
        requestTimeoutMs: server.requestTimeoutMs,
        enabled: server.enabled,
      }
    }

    return {
      id: server.id,
      name: server.name,
      transport: 'stdio',
      command: server.command,
      args: server.args,
      env: server.env,
      cwd: server.cwd,
      autoRestart: server.autoRestart,
      requestTimeoutMs: server.requestTimeoutMs,
      restartDelayMs: server.restartDelayMs,
      maxRestarts: server.maxRestarts,
      restartWindowMs: server.restartWindowMs,
      enabled: server.enabled,
    }
  })
}
