/** Local provider loaders — Ollama, LM Studio, vLLM, llama.cpp, text-generation-webui */

import os from "os"
import { Effect } from "effect"
import { InstallationVersion } from "@saeeol/core/installation/version"
import type { CustomDep, CustomLoader } from "./provider-types"
import type { Info } from "./provider-schema"
import { useLanguageModel } from "./bundled-providers"
import { iife } from "@/util/iife"

function ua(name: string): string {
  return `saeeol/${InstallationVersion} ${name} (${os.platform()} ${os.release()}; ${os.arch()})`
}

export function localLoaders(dep: CustomDep): Record<string, CustomLoader> {
  return {
    ollama: Effect.fnUntraced(function* (_provider: Info) {
      const env = yield* dep.env()
      const endpoint = iife(() => {
        return [
          _provider.options?.baseURL,
          env["OLLAMA_BASE_URL"],
          env["OLLAMA_HOST"],
        ].find((url) => typeof url === "string" && url.trim() !== "")
      }) ?? "http://localhost:11434"

      return {
        autoload: true,
        options: {
          baseURL: endpoint,
          headers: { "User-Agent": ua("ollama") },
        },
        vars(): Record<string, string> {
          return { OLLAMA_BASE_URL: endpoint }
        },
        async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
          if (useLanguageModel(sdk)) return sdk.languageModel(modelID)
          return sdk.chat(modelID)
        },
        async discoverModels() {
          try {
            const res = await fetch(`${endpoint}/api/tags`, { signal: AbortSignal.timeout(5000) })
            if (!res.ok) return {}
            const data = (await res.json()) as { models?: Array<{ name: string }> }
            const models: Record<string, any> = {}
            for (const m of data.models ?? []) {
              models[m.name] = {
                id: m.name, name: m.name, providerID: "ollama",
                api: { id: m.name, url: `${endpoint}/v1`, npm: "@ai-sdk/openai-compatible" },
                capabilities: { temperature: true, reasoning: false, attachment: false, toolcall: true, input: { text: true, image: false, audio: false, video: false, pdf: false }, output: { text: true, image: false, audio: false, video: false, pdf: false }, interleaved: false },
                cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
                limit: { context: 32768, output: 4096 },
                status: "active" as const, options: {}, headers: {}, release_date: "",
              }
            }
            return models
          } catch {
            return {}
          }
        },
      }
    }),

    lmstudio: Effect.fnUntraced(function* (_provider: Info) {
      const env = yield* dep.env()
      const endpoint = iife(() => {
        return [
          _provider.options?.baseURL,
          env["LMSTUDIO_BASE_URL"],
        ].find((url) => typeof url === "string" && url.trim() !== "")
      }) ?? "http://localhost:1234/v1"

      return {
        autoload: true,
        options: {
          baseURL: endpoint,
          headers: { "User-Agent": ua("lmstudio") },
        },
        vars(): Record<string, string> {
          return { LMSTUDIO_BASE_URL: endpoint }
        },
        async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
          if (useLanguageModel(sdk)) return sdk.languageModel(modelID)
          return sdk.chat(modelID)
        },
        async discoverModels() {
          try {
            const res = await fetch(`${endpoint}/models`, { signal: AbortSignal.timeout(5000) })
            if (!res.ok) return {}
            const data = (await res.json()) as { data?: Array<{ id: string }> }
            const models: Record<string, any> = {}
            for (const m of data.data ?? []) {
              models[m.id] = {
                id: m.id, name: m.id, providerID: "lmstudio",
                api: { id: m.id, url: endpoint, npm: "@ai-sdk/openai-compatible" },
                capabilities: { temperature: true, reasoning: false, attachment: false, toolcall: true, input: { text: true, image: false, audio: false, video: false, pdf: false }, output: { text: true, image: false, audio: false, video: false, pdf: false }, interleaved: false },
                cost: { input: 0, output: 0, cache: { read: 0, write: 0 } },
                limit: { context: 32768, output: 4096 },
                status: "active" as const, options: {}, headers: {}, release_date: "",
              }
            }
            return models
          } catch {
            return {}
          }
        },
      }
    }),

    vllm: Effect.fnUntraced(function* (_provider: Info) {
      const env = yield* dep.env()
      const endpoint = iife(() => {
        return [
          _provider.options?.baseURL,
          env["VLLM_BASE_URL"],
        ].find((url) => typeof url === "string" && url.trim() !== "")
      }) ?? "http://localhost:8000/v1"

      return {
        autoload: true,
        options: {
          baseURL: endpoint,
          headers: { "User-Agent": ua("vllm") },
        },
        vars(): Record<string, string> {
          return { VLLM_BASE_URL: endpoint }
        },
        async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
          if (useLanguageModel(sdk)) return sdk.languageModel(modelID)
          return sdk.chat(modelID)
        },
      }
    }),

    "text-generation-webui": Effect.fnUntraced(function* (_provider: Info) {
      const env = yield* dep.env()
      const endpoint = iife(() => {
        return [
          _provider.options?.baseURL,
          env["TEXT_GEN_WEBUI_BASE_URL"],
        ].find((url) => typeof url === "string" && url.trim() !== "")
      }) ?? "http://localhost:5000"

      return {
        autoload: true,
        options: {
          baseURL: endpoint,
          headers: { "User-Agent": ua("text-generation-webui") },
        },
        vars(): Record<string, string> {
          return { TEXT_GEN_WEBUI_BASE_URL: endpoint }
        },
        async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
          if (useLanguageModel(sdk)) return sdk.languageModel(modelID)
          return sdk.chat(modelID)
        },
      }
    }),

    "llama.cpp": Effect.fnUntraced(function* (_provider: Info) {
      const env = yield* dep.env()
      const endpoint = iife(() => {
        return [
          _provider.options?.baseURL,
          env["LLAMA_CPP_BASE_URL"],
        ].find((url) => typeof url === "string" && url.trim() !== "")
      }) ?? "http://localhost:8080"

      return {
        autoload: true,
        options: {
          baseURL: endpoint,
          headers: { "User-Agent": ua("llama.cpp") },
        },
        vars(): Record<string, string> {
          return { LLAMA_CPP_BASE_URL: endpoint }
        },
        async getModel(sdk: any, modelID: string, _options?: Record<string, any>) {
          if (useLanguageModel(sdk)) return sdk.languageModel(modelID)
          return sdk.chat(modelID)
        },
      }
    }),
  }
}
