// Shared LLM model runtime + selection for every model-backed feature (URL prefill, roast
// evaluation). One ModelRuntime per process; which model actually runs is decided here:
// the admin-chosen setting (app_settings.llm_model, passed in as `preferred`) wins, then the
// LLM_MODEL / PREFILL_MODEL env overrides, then the first available configured model.

import { ModelRuntime } from "@earendil-works/pi-coding-agent";

let modelRuntimePromise = null;
export async function getModelRuntime() {
  if (!modelRuntimePromise) modelRuntimePromise = ModelRuntime.create();
  return modelRuntimePromise;
}

export const modelKey = (model) => `${model.provider}:${model.id}`;

/** Every configured model, as stable "provider:id" keys for the admin picker. */
export async function listAvailableModels() {
  const runtime = await getModelRuntime();
  return (await runtime.getAvailable()).map((model) => ({
    key: modelKey(model),
    name: model.name ?? model.id,
    provider: model.provider,
  }));
}

/** @param {string|null} preferred "provider:id" from the admin setting, or empty for auto. */
export async function pickModel(preferred = null) {
  const runtime = await getModelRuntime();
  for (const candidate of [preferred, process.env.LLM_MODEL, process.env.PREFILL_MODEL]) {
    if (!candidate) continue;
    const sep = candidate.indexOf(":");
    if (sep === -1) continue;
    const model = runtime.getModel(candidate.slice(0, sep), candidate.slice(sep + 1));
    if (model) return model;
  }
  return (await runtime.getAvailable())[0];
}

/** Consistent error for "the server has no usable model" across features. */
export function noModelError() {
  const err = new Error("No LLM model is configured on the server.");
  err.code = "no_model";
  return err;
}
