// Verifies that:
//   1. ModelCache.failedProviders() surfaces providers that encountered errors.
//   2. ModelCache.getFailure() returns the typed error for a failed provider.
//   3. Clear removes failure state.

import { test, expect, mock } from "bun:test"
import path from "path"
import * as Log from "@saeeol/core/util/log"

Log.init({ print: false })

// Stub fetchSaeeolModels to return controlled typed results.
let stubbedResult: { models: Record<string, any>; error?: { kind: string; status?: number } } = { models: {} }

mock.module("@saeeol/gateway", () => ({
  fetchSaeeolModels: async () => stubbedResult,
  SAEEOL_OPENROUTER_BASE: "https://api.saeeol.ai/api/openrouter",
}))

mock.module("saeeol-copilot-auth", () => ({ default: () => ({}) }))
mock.module("saeeol-anthropic-auth", () => ({ default: () => ({}) }))
mock.module("@gitlab/saeeol-gitlab-auth", () => ({ default: () => ({}) }))

import { tmpdir } from "../fixture/fixture"
import { Instance } from "../../src/project/instance"
import { ModelCache } from "../../src/provider/model-cache"

const CONFIG = JSON.stringify({ $schema: "https://app.saeeol.ai/config.json" })

async function withInstance<T>(fn: () => Promise<T>): Promise<T> {
  await using tmp = await tmpdir({
    init: async (dir) => {
      await Bun.write(path.join(dir, "saeeol.json"), CONFIG)
    },
  })
  return Instance.provide({ directory: tmp.path, fn })
}

test("failedProviders returns empty array when no fetch has occurred", () => {
  ModelCache.clear("saeeol")
  expect(ModelCache.failedProviders()).not.toContain("saeeol")
})

test("getFailure returns undefined when fetch succeeds", async () => {
  stubbedResult = {
    models: {
      "test/model": {
        id: "test/model",
        name: "Test",
        cost: { input: 1, output: 2 },
        limit: { context: 128000, output: 4096 },
      },
    },
  }
  ModelCache.clear("saeeol")
  await withInstance(() => ModelCache.fetch("saeeol"))
  expect(ModelCache.getFailure("saeeol")).toBeUndefined()
  expect(ModelCache.failedProviders()).not.toContain("saeeol")
})

test("failedProviders includes provider after auth error", async () => {
  stubbedResult = { models: {}, error: { kind: "unauthorized", status: 401 } }
  ModelCache.clear("saeeol")
  await withInstance(() => ModelCache.fetch("saeeol"))
  expect(ModelCache.failedProviders()).toContain("saeeol")
  expect(ModelCache.getFailure("saeeol")).toMatchObject({ kind: "unauthorized", status: 401 })
})

test("clear removes failure state", async () => {
  stubbedResult = { models: {}, error: { kind: "network" } }
  ModelCache.clear("saeeol")
  await withInstance(() => ModelCache.fetch("saeeol"))
  expect(ModelCache.failedProviders()).toContain("saeeol")

  ModelCache.clear("saeeol")
  expect(ModelCache.failedProviders()).not.toContain("saeeol")
  expect(ModelCache.getFailure("saeeol")).toBeUndefined()
})

test("failure state is cleared when subsequent fetch succeeds", async () => {
  stubbedResult = { models: {}, error: { kind: "unauthorized", status: 401 } }
  ModelCache.clear("saeeol")
  await withInstance(() => ModelCache.fetch("saeeol"))
  expect(ModelCache.failedProviders()).toContain("saeeol")

  stubbedResult = {
    models: {
      "test/model": {
        id: "test/model",
        name: "Test",
        cost: { input: 1, output: 2 },
        limit: { context: 128000, output: 4096 },
      },
    },
  }
  ModelCache.clear("saeeol")
  await withInstance(() => ModelCache.fetch("saeeol"))
  expect(ModelCache.failedProviders()).not.toContain("saeeol")
  expect(ModelCache.getFailure("saeeol")).toBeUndefined()
})
