import { assert, describe, it } from "@effect/vitest"
import { Effect } from "effect"
import * as Reactivity from "effect/unstable/reactivity/Reactivity"
import { vi } from "vitest"

const state: {
  connectCause: unknown
  queryCause: unknown
} = {
  connectCause: null,
  queryCause: null
}

vi.mock("@clickhouse/client", () => ({
  createClient: () => ({
    exec: () => state.connectCause ? Promise.reject(state.connectCause) : Promise.resolve({}),
    close: () => Promise.resolve(),
    query: () =>
      state.queryCause
        ? Promise.reject(state.queryCause)
        : Promise.resolve({
          json: () => Promise.resolve({ data: [] })
        }),
    command: () => Promise.resolve({}),
    insert: () => Promise.resolve({})
  })
}))

const connectFailureReasonTag = (code: number) =>
  Effect.gen(function*() {
    state.connectCause = { code }
    state.queryCause = null
    const { ClickhouseClient } = yield* Effect.promise(() => import("@effect/sql-clickhouse"))
    const error = yield* Effect.flip(ClickhouseClient.make({ url: "http://localhost:8123" }))
    return error.reason._tag
  }).pipe(
    Effect.scoped,
    Effect.provide(Reactivity.layer)
  )

const queryFailureReasonTag = (code: number) =>
  Effect.gen(function*() {
    state.connectCause = null
    state.queryCause = { code }
    const { ClickhouseClient } = yield* Effect.promise(() => import("@effect/sql-clickhouse"))
    const client = yield* ClickhouseClient.make({ url: "http://localhost:8123" })
    const error = yield* Effect.flip(client`SELECT 1`)
    return error.reason._tag
  }).pipe(
    Effect.scoped,
    Effect.provide(Reactivity.layer)
  )

describe("ClickhouseClient SqlError classification", () => {
  it.effect("maps representative native codes to reasons", () =>
    Effect.gen(function*() {
      const cases = [
        [516, "AuthenticationError"],
        [497, "AuthorizationError"],
        [62, "SqlSyntaxError"],
        [159, "StatementTimeoutError"]
      ] as const

      for (const [code, expectedTag] of cases) {
        const tag = yield* connectFailureReasonTag(code)
        assert.strictEqual(tag, expectedTag)
      }
    }))

  it.effect("falls back to UnknownError for unmapped codes during execution", () =>
    Effect.gen(function*() {
      const tag = yield* queryFailureReasonTag(999)
      assert.strictEqual(tag, "UnknownError")
    }))
})
