import { describe, expect } from "bun:test"
import { ConfigProvider, Effect } from "effect"
import { Headers } from "effect/unstable/http"
import { LLM } from "../src"
import { Auth } from "../src/route/auth"
import * as OpenAIChat from "../src/protocols/openai-chat"
import { Model } from "../src/schema"
import { it } from "./lib/effect"

const request = LLM.request({
  id: "req_auth",
  model: Model.make({ id: "fake-model", provider: "fake", route: OpenAIChat.route }),
  prompt: "hello",
})

const input = {
  request,
  method: "POST" as const,
  url: "https://example.test/v1/chat",
  body: "{}",
  headers: Headers.fromInput({ "x-existing": "yes" }),
}

const withEnv = (env: Record<string, string>) => Effect.provide(ConfigProvider.layer(ConfigProvider.fromEnv({ env })))

describe("Auth", () => {
  it.effect("renders a config credential as bearer auth", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.config("OPENAI_API_KEY")
        .bearer()
        .apply(input)
        .pipe(withEnv({ OPENAI_API_KEY: "sk-test" }))

      expect(headers.authorization).toBe("Bearer sk-test")
      expect(headers["x-existing"]).toBe("yes")
    }),
  )

  it.effect("falls back between credential sources before rendering", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.config("PRIMARY_KEY")
        .orElse(Auth.value("fallback-key"))
        .pipe(Auth.header("x-api-key"))
        .apply(input)
        .pipe(withEnv({}))

      expect(headers["x-api-key"]).toBe("fallback-key")
      expect(headers["x-existing"]).toBe("yes")
    }),
  )

  it.effect("composes header auth in sequence", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.headers({ "x-tenant-id": "tenant-1" })
        .andThen(Auth.bearer("gateway-token"))
        .apply(input)

      expect(headers["x-tenant-id"]).toBe("tenant-1")
      expect(headers.authorization).toBe("Bearer gateway-token")
      expect(headers["x-existing"]).toBe("yes")
    }),
  )

  it.effect("renders a direct secret as a custom header", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.header("api-key", "direct-key").apply(input)

      expect(headers["api-key"]).toBe("direct-key")
      expect(headers["x-existing"]).toBe("yes")
    }),
  )

  it.effect("renders bearer auth into a custom header", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.bearerHeader("cf-aig-authorization", "gateway-token").apply(input)

      expect(headers["cf-aig-authorization"]).toBe("Bearer gateway-token")
      expect(headers["x-existing"]).toBe("yes")
    }),
  )

  it.effect("falls back between full auth values", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.config("OPENAI_API_KEY")
        .bearer()
        .orElse(Auth.headers({ authorization: "Bearer supplied" }))
        .apply(input)
        .pipe(withEnv({}))

      expect(headers.authorization).toBe("Bearer supplied")
      expect(headers["x-existing"]).toBe("yes")
    }),
  )

  it.effect("can intentionally leave auth untouched", () =>
    Effect.gen(function* () {
      const headers = yield* Auth.none.apply(input)

      expect(headers.authorization).toBeUndefined()
      expect(headers["x-existing"]).toBe("yes")
    }),
  )
})
