/* eslint-disable @typescript-eslint/no-explicit-any */
import * as Test from "@effect-ts/jest/Test"
import { describe, expect } from "@jest/globals"

import { OptimisticConcurrencyException } from "../../errors.js"

import { makeMemoryStore } from "./Memory.js"

const { it } = Test.runtime()

describe("Optimistic Concurrency", () => {
  describe("set", () => {
    it("works", () =>
      Effect.gen(function*($) {
        const existing = new Map<
          string,
          { _etag?: string | undefined; id: string; a: string }
        >()
        const { make } = makeMemoryStore()
        const store = yield* $(make("test", Effect.sync(() => existing)))

        const result1 = yield* $(store.set({ id: "1", a: "string", _etag: undefined }))
        const result2_1 = yield* $(
          store.set({ id: "2", a: "string", _etag: undefined })
        )
        const result2 = yield* $(store.set({ ...result1, a: "another-string" }))
        const result3 = yield* $(store.set({ ...result2, a: "another-string2" }))
        const failResult = yield* $(
          store.set({ ...result1, a: "another-string3" }).result
        )
        expect(result1._etag).toBeTruthy()
        expect(result2_1._etag).toBeTruthy()
        expect(result2._etag).toBeTruthy()
        expect(result3._etag).toBeTruthy()

        expect(result2._etag).not.toEqual(result1._etag)
        expect(result3._etag).not.toEqual(result2._etag)

        expect(Exit.untraced(failResult)).toEqual(
          Exit.fail(new OptimisticConcurrencyException("test", "1"))
        )
        console.log({ result1, result2 })
      }))
  })

  describe("bulkSet", () => {
    it("works", () =>
      Effect.gen(function*($) {
        const existing = new Map<
          string,
          { _etag?: string | undefined; id: string; a: string }
        >()
        const { make } = makeMemoryStore()
        const store = yield* $(make("test", Effect.sync(() => existing)))

        const [result1] = yield* $(
          store.bulkSet([{ id: "1", a: "string", _etag: undefined }])
        )
        const [result2_1] = yield* $(
          store.bulkSet([{ id: "2", a: "string", _etag: undefined }])
        )
        const [result2] = yield* $(store.bulkSet([{ ...result1, a: "another-string" }]))
        const [result3] = yield* $(
          store.bulkSet([{ ...result2, a: "another-string2" }])
        )
        const failResult = yield* $(
          store.bulkSet([{ ...result1, a: "another-string3" }]).result
        )
        expect(result1._etag).toBeTruthy()
        expect(result2_1._etag).toBeTruthy()
        expect(result2._etag).toBeTruthy()
        expect(result3._etag).toBeTruthy()

        expect(result2._etag).not.toEqual(result1._etag)
        expect(result3._etag).not.toEqual(result2._etag)

        expect(Exit.untraced(failResult)).toEqual(
          Exit.fail(new OptimisticConcurrencyException("test", "1"))
        )
        console.log({ result1, result2 })
      }))
  })

  describe("batchSet", () => {
    it("works", () =>
      Effect.gen(function*($) {
        const existing = new Map<
          string,
          { _etag?: string | undefined; id: string; a: string }
        >()
        const { make } = makeMemoryStore()
        const store = yield* $(make("test", Effect.sync(() => existing)))

        const item1 = { id: "1", a: "string", _etag: undefined }
        const item2 = { id: "2", a: "string", _etag: undefined }

        yield* $(store.batchSet([item1, item2]))

        const [result1, result2] = yield* $(
          Effect.collectAll([store.find("1"), store.find("2")])
        )
        const failResult = yield* $(
          store.batchSet([{ id: "1", a: "another-string3", _etag: "abc" }]).result
        )
        expect(result1).toEqual(Option.some({ ...item1, _etag: expect.any(String) }))
        expect(result2).toEqual(Option.some({ ...item2, _etag: expect.any(String) }))
        expect(Exit.untraced(failResult)).toEqual(
          Exit.fail(new OptimisticConcurrencyException("test", "1"))
        )
      }))
  })
})
