import type { FallbackEntry } from "./model-requirement-types.js"
import type { FallbackModelObject } from "./fallback-model-object.js"
import { normalizeFallbackModels } from "./model-resolver.js"
import { KNOWN_VARIANTS } from "./known-variants.js"

function parseVariantFromModel(rawModel: string): { modelID: string; variant?: string } {
  if (typeof rawModel !== "string") {
    return { modelID: "" }
  }
  const trimmedModel = rawModel.trim()
  if (!trimmedModel) {
    return { modelID: "" }
  }

  const parenthesizedVariant = trimmedModel.match(/^(.*)\(([^()]+)\)\s*$/)
  if (parenthesizedVariant) {
    const modelID = parenthesizedVariant[1]?.trim() ?? ""
    const variant = parenthesizedVariant[2]?.trim()
    return variant ? { modelID, variant } : { modelID }
  }

  const spaceVariant = trimmedModel.match(/^(.*\S)\s+([a-z][a-z0-9_-]*)$/i)
  if (spaceVariant) {
    const modelID = spaceVariant[1]?.trim() ?? ""
    const variant = spaceVariant[2]?.trim().toLowerCase()
    if (variant && KNOWN_VARIANTS.has(variant)) {
      return { modelID, variant }
    }
  }

  return { modelID: trimmedModel }
}

export function parseFallbackModelEntry(
  model: string,
  contextProviderID: string | undefined,
  defaultProviderID = "opencode",
): FallbackEntry | undefined {
  if (typeof model !== "string") return undefined
  const trimmed = model.trim()
  if (!trimmed) return undefined

  const parts = trimmed.split("/")
  const providerID =
    parts.length >= 2 ? (parts[0]?.trim() ?? "") : (contextProviderID?.trim() || defaultProviderID)
  const rawModelID = parts.length >= 2 ? parts.slice(1).join("/").trim() : trimmed
  if (!providerID || !rawModelID) return undefined

  const parsed = parseVariantFromModel(rawModelID)
  if (!parsed.modelID) return undefined

  return {
    providers: [providerID],
    model: parsed.modelID,
    variant: parsed.variant,
  }
}

export function parseFallbackModelObjectEntry(
  obj: FallbackModelObject,
  contextProviderID: string | undefined,
  defaultProviderID = "opencode",
): FallbackEntry | undefined {
  const base = parseFallbackModelEntry(obj.model, contextProviderID, defaultProviderID)
  if (!base) return undefined

  return {
    ...base,
    variant: obj.variant ?? base.variant,
    reasoningEffort: obj.reasoningEffort,
    temperature: obj.temperature,
    top_p: obj.top_p,
    maxTokens: obj.maxTokens,
    thinking: obj.thinking,
  }
}

/**
 * Find the most specific FallbackEntry whose `provider/model` is a prefix of
 * the resolved `provider/modelID`.  Longest match wins so that e.g.
 * `openai/gpt-5.4-preview` picks the entry for `openai/gpt-5.4-preview` over
 * the shorter `openai/gpt-5.4`.
 */
export function findMostSpecificFallbackEntry(
  providerID: string,
  modelID: string,
  chain: FallbackEntry[],
): FallbackEntry | undefined {
  const resolved = `${providerID}/${modelID}`.toLowerCase()

  // Collect entries whose provider/model is a prefix of the resolved model,
  // together with the length of the matching prefix (longest match wins).
  const matches: { entry: FallbackEntry; matchLen: number }[] = []
  for (const entry of chain) {
    for (const p of entry.providers) {
      const candidate = `${p}/${entry.model}`.toLowerCase()
      if (resolved.startsWith(candidate)) {
        matches.push({ entry, matchLen: candidate.length })
        break // one match per entry is enough
      }
    }
  }

  if (matches.length === 0) return undefined
  matches.sort((a, b) => b.matchLen - a.matchLen)
  return matches[0]!.entry
}

export function buildFallbackChainFromModels(
  fallbackModels: string | (string | FallbackModelObject)[] | undefined,
  contextProviderID: string | undefined,
  defaultProviderID = "opencode",
): FallbackEntry[] | undefined {
  const normalized = normalizeFallbackModels(fallbackModels)
  if (!normalized || normalized.length === 0) return undefined

  const parsed = normalized
    .map((entry) => {
      if (typeof entry === "string") {
        return parseFallbackModelEntry(entry, contextProviderID, defaultProviderID)
      }
      return parseFallbackModelObjectEntry(entry, contextProviderID, defaultProviderID)
    })
    .filter((entry): entry is FallbackEntry => entry !== undefined)

  if (parsed.length === 0) return undefined
  return parsed
}
