import { Effect, Schema } from "effect"
import { Npm } from "@saeeol/core/npm"
import * as Bus from "@/bus"
import * as Tool from "../core/tool"
import { ProviderInstallEvent } from "@/provider/provider-events"

const Parameters = Schema.Struct({
  action: Schema.Literals(["install", "list"]).annotate({
    description: '"install" to install provider packages, "list" to show available providers',
  }),
  packages: Schema.optional(Schema.Array(Schema.String)).annotate({
    description:
      'Package names to install. npm names like "@ai-sdk/anthropic" or short names like "anthropic".',
  }),
})

type Params = Schema.Schema.Type<typeof Parameters>

const SHORT_NAME_MAP: Record<string, string> = {
  anthropic: "@ai-sdk/anthropic",
  openai: "@ai-sdk/openai",
  "openai-compatible": "@ai-sdk/openai-compatible",
  google: "@ai-sdk/google",
  "google-vertex": "@ai-sdk/google-vertex",
  bedrock: "@ai-sdk/amazon-bedrock",
  "amazon-bedrock": "@ai-sdk/amazon-bedrock",
  azure: "@ai-sdk/azure",
  openrouter: "@openrouter/ai-sdk-provider",
  groq: "@ai-sdk/groq",
  deepinfra: "@ai-sdk/deepinfra",
  gateway: "@ai-sdk/gateway",
  alibaba: "@ai-sdk/alibaba",
  cerebras: "@ai-sdk/cerebras",
  xai: "@ai-sdk/xai",
  mistral: "@ai-sdk/mistral",
  cohere: "@ai-sdk/cohere",
  togetherai: "@ai-sdk/togetherai",
  perplexity: "@ai-sdk/perplexity",
  vercel: "@ai-sdk/vercel",
  gitlab: "gitlab-ai-provider",
  venice: "venice-ai-sdk-provider",
}

const KNOWN_PROVIDERS = [
  { npm: "@saeeol/gateway", label: "Saeeol Gateway (bundled)" },
  { npm: "@ai-sdk/anthropic", label: "Anthropic (Claude)" },
  { npm: "@ai-sdk/openai", label: "OpenAI (GPT)" },
  { npm: "@ai-sdk/openai-compatible", label: "OpenAI Compatible" },
  { npm: "@ai-sdk/google", label: "Google (Gemini)" },
  { npm: "@ai-sdk/github-copilot", label: "GitHub Copilot" },
  { npm: "@ai-sdk/amazon-bedrock", label: "Amazon Bedrock" },
  { npm: "@ai-sdk/azure", label: "Azure OpenAI" },
  { npm: "@ai-sdk/google-vertex", label: "Google Vertex AI" },
  { npm: "@openrouter/ai-sdk-provider", label: "OpenRouter" },
  { npm: "@ai-sdk/groq", label: "Groq" },
  { npm: "@ai-sdk/deepinfra", label: "DeepInfra" },
  { npm: "@ai-sdk/gateway", label: "Vercel AI Gateway" },
  { npm: "@ai-sdk/alibaba", label: "Alibaba (Qwen)" },
  { npm: "@ai-sdk/cerebras", label: "Cerebras" },
  { npm: "@ai-sdk/xai", label: "xAI (Grok)" },
  { npm: "@ai-sdk/mistral", label: "Mistral" },
  { npm: "@ai-sdk/cohere", label: "Cohere" },
  { npm: "@ai-sdk/togetherai", label: "Together AI" },
  { npm: "@ai-sdk/perplexity", label: "Perplexity" },
  { npm: "@ai-sdk/vercel", label: "Vercel" },
]

function resolve(pkg: string) {
  if (pkg.startsWith("@") || pkg.startsWith("file://")) return pkg
  return SHORT_NAME_MAP[pkg] ?? pkg
}

export const PackageTool = Tool.define(
  "package",
  Effect.gen(function* () {
    return {
      description: [
        "Manage saeeol provider SDK packages.",
        "",
        "Use this tool when the user asks to install additional AI providers,",
        "or when you detect that a required provider is not available.",
        "",
        'Actions: "install" — install provider packages, "list" — show available providers',
        "",
        "Short names: anthropic, openai, google, bedrock, azure, openrouter, groq,",
        "deepinfra, cerebras, xai, mistral, cohere, togetherai, perplexity, vercel",
      ].join("\n"),
      parameters: Parameters,
      execute: (params: Params, ctx: Tool.Context) =>
        Effect.gen(function* () {
          if (params.action === "list") {
            const lines = [
              "Available provider SDKs:",
              "",
              ...KNOWN_PROVIDERS.map((p) => `  ${p.label}\n    npm: ${p.npm}`),
              "",
              'Install: { action: "install", packages: ["anthropic"] }',
            ]
            return { title: "Available Providers", output: lines.join("\n"), metadata: {} }
          }
          const packages = params.packages
          if (!packages || packages.length === 0) {
            return {
              title: "Package Install",
              output: "No packages specified. Provide package names in the 'packages' array.",
              metadata: {},
            }
          }

          const resolved = packages.map(resolve)

          yield* ctx.ask({
            permission: "package",
            patterns: resolved,
            always: resolved,
            metadata: {},
          })

          const results: string[] = []
          for (const pkg of resolved) {
            void Bus.publish(ProviderInstallEvent.Started, {
              providerID: pkg,
              pkg,
            })
            try {
              const entry = yield* Effect.promise(() => Npm.add(pkg))
              if (!entry.entrypoint) {
                void Bus.publish(ProviderInstallEvent.Failed, {
                  providerID: pkg,
                  pkg,
                  error: "No import entrypoint found",
                })
                results.push(`X ${pkg}: no import entrypoint`)
                continue
              }
              void Bus.publish(ProviderInstallEvent.Completed, {
                providerID: pkg,
                pkg,
              })
              results.push(`OK ${pkg}`)
            } catch (err) {
              const msg = err instanceof Error ? err.message : String(err)
              void Bus.publish(ProviderInstallEvent.Failed, {
                providerID: pkg,
                pkg,
                error: msg,
              })
              results.push(`X ${pkg}: ${msg}`)
            }
          }

          return {
            title: "Package Install",
            output: [
              `${resolved.length} package(s) processed:`,
              "",
              ...results,
              "",
              "Installed providers are now available.",
            ].join("\n"),
            metadata: {},
          }
        }),
    }
  }),
)