import {
  _localModelsSupported,
  _resolveModelName,
  _downloadModel,
  _listDownloadedModels,
  _listModelNames,
  _aliasModel,
  _unaliasModel,
  _removeModel,
  _registerLocalProvider,
  _registerLocalModel,
  _printLocalCatalog,
  _refreshCatalog,
 } from "agency-lang/stdlib-lib/localModels.js"

/** @module
  @summary Manage and run local GGUF models: download by short name or Hugging Face URI, alias them, and register the local provider.
  Manage and run local GGUF models. Download models by curated short name or
  Hugging Face URI, alias them, list or remove downloads, and register the local
  provider so `llm()` calls can use them. Requires the smoltalk-llama-cpp
  package.

  ```ts
  import { registerLocalModel } from "std::agency/local"

  node main() {
    // download if needed, register the provider, and get the local path
    const model = registerLocalModel("smollm2-135m")
    print(model)
  }
  ```
*/

export type DownloadedModel = {
  name: string;
  path: string;
  sizeBytes: number
}

export type ModelName = {
  name: string;
  target: string;
  source: string;
  params?: string;
  sizeBytes?: number;
  category?: string;
  description?: string;
  contextWindow?: number;
  license?: string
}

export def localModelsSupported(): bool {
  """
  True if smoltalk-llama-cpp is installed.
  """
  return _localModelsSupported()
}

export def resolveModelName(value: string): string {
  """
  Map a curated short name or alias to its Hugging Face URI. Pass URIs and
  .gguf paths through unchanged.

  @param value - name, alias, hf: URI, or .gguf path
  """
  return _resolveModelName(value)
}

export def downloadModel(value: string, cacheDir: string = ""): string {
  """
  Download a model and return its local .gguf path.
  Skips the download if the file already exists in the cache dir.

  @param value - what to download
  @param cacheDir - download dir (empty string = per-user cache)
  """
  return _downloadModel(value, cacheDir)
}

export def listDownloadedModels(cacheDir: string = ""): DownloadedModel[] {
  """
  List downloaded .gguf models.

  @param cacheDir - models dir (empty string = per-user cache)
  """
  return _listDownloadedModels(cacheDir)
}

export def listModelNames(): ModelName[] {
  """
  List usable short names: curated built-ins and your aliases.
  """
  return _listModelNames()
}

export def aliasModel(name: string, uri: string): string {
  """
  Add a short-name alias for a model URI
  Returns the path to the agency.json file that the alias was written to.

  @param name - the alias
  @param uri - the hf: URI it maps to
  """
  return _aliasModel(name, uri)
}

export def unaliasModel(name: string): string {
  """
  Remove a short-name alias.
  Returns the path to the agency.json file that was modified.

  @param name - the alias to remove
  """
  return _unaliasModel(name).file
}

export def removeModel(name: string, cacheDir: string = ""): bool {
  """
  Delete a downloaded model file.
  Returns false if the model was not present.

  @param name - the .gguf filename
  @param cacheDir - models dir (empty string = per-user cache)
  """
  return _removeModel(name, cacheDir)
}

export def registerLocalProvider() {
  """
  Register the llama-cpp provider so local models can be used for LLM calls.
  """
  _registerLocalProvider()
}

export def registerLocalModel(value: string, cacheDir: string = ""): string {
  """
  Register the provider and ensure the model is downloaded. Returns the local
  .gguf path to use as the model for LLM calls with provider "llama-cpp".

  @param value - name, alias, hf: URI, or .gguf path
  @param cacheDir - download dir (empty string = per-user cache)
  """
  return _registerLocalModel(value, cacheDir)
}

export def printLocalCatalog() {
  """
  Print the usable-model catalog (curated names + your aliases) as an
    aligned table, the same listing as `agency local alias list`.
  """
  _printLocalCatalog()
}

/** Used when refreshing the model catalog. If a catalog model's name collides with one of your own aliases,
    we'll keep your alias and skip the catalog entry. This type describes what was skipped. */
export type SkippedAlias = {
  name: string;
  keptUri: string;
  remoteUri: string
}

/** The outcome of `refreshCatalog`. modelCount = total catalog size. */
export type RefreshResult = {
  url: string;
  file: string;
  added: string[];
  updated: string[];
  unchanged: string[];
  removed: string[];
  skipped: SkippedAlias[];
  modelCount: number
}

/** Same operation as the `agency local refresh` CLI command. */
export def refreshCatalog(url: string = ""): RefreshResult {
  """
  Fetch the remote model catalog and update the `source:"remote"` aliases in
  the nearest `agency.json` from it. Adds/updates models from the catalog,
  removes ones it dropped, and skips any name you've aliased
  yourself. Your hand-added aliases are never overwritten. Throws on a
  fetch/parse/validation failure, leaving `agency.json` untouched.

  @param url - catalog URL override; empty string uses the
    `AGENCY_MODEL_CATALOG_URL` env var, then `client.modelCatalogUrl` in
    `agency.json`, then the built-in default.
  """
  return _refreshCatalog({
    url: url
  })
}
