import { addEqualityTesters, afterEach, assert, beforeEach, describe, expect, it, test, vitest } from "@effect/vitest"
import {
  Array as Arr,
  Cause,
  Context,
  Effect,
  Hash,
  Latch,
  Layer,
  Option,
  Result,
  Schema,
  Stream,
  SubscriptionRef
} from "effect"
import { TestClock } from "effect/testing"
import { KeyValueStore } from "effect/unstable/persistence"
import { AsyncResult, Atom, AtomRegistry } from "effect/unstable/reactivity"

declare const global: any

addEqualityTesters()

describe.sequential("Atom", () => {
  beforeEach(async () => {
    vitest.useFakeTimers({
      toFake: [
        "Date",
        "hrtime",
        "setTimeout",
        "clearTimeout",
        "setInterval",
        "clearInterval",
        "performance"
      ]
    })
    await Effect.runPromise(Effect.yieldNow)
  })
  afterEach(() => {
    vitest.useRealTimers()
  })

  it("get/set", () => {
    const counter = Atom.make(0)
    const r = AtomRegistry.make()
    expect(r.get(counter)).toEqual(0)
    r.set(counter, 1)
    expect(r.get(counter)).toEqual(1)
  })

  it("keepAlive false", async () => {
    const counter = Atom.make(0)
    const r = AtomRegistry.make()
    r.set(counter, 1)
    expect(r.get(counter)).toEqual(1)
    await Effect.runPromise(Effect.yieldNow)
    expect(r.get(counter)).toEqual(0)
  })

  it("keepAlive true", async () => {
    const counter = Atom.make(0).pipe(
      Atom.keepAlive
    )
    const r = AtomRegistry.make()
    r.set(counter, 1)
    expect(r.get(counter)).toEqual(1)
    await new Promise((resolve) => resolve(null))
    expect(r.get(counter)).toEqual(1)
  })

  it("subscribe", async () => {
    const counter = Atom.make(0)
    const r = AtomRegistry.make()
    let count = 0
    const cancel = r.subscribe(counter, (_) => {
      count = _
    })
    r.set(counter, 1)
    expect(count).toEqual(1)
    await Effect.runPromise(Effect.yieldNow)

    expect(r.get(counter)).toEqual(1)
    cancel()
    await Effect.runPromise(Effect.yieldNow)
    expect(r.get(counter)).toEqual(0)
  })

  it("subscribe does not skip listeners when unsubscribing during notify", () => {
    const counter = Atom.make(0)
    const r = AtomRegistry.make()
    let first = 0
    let second = 0
    let cancelFirst = () => {
    }

    cancelFirst = r.subscribe(counter, () => {
      first++
      cancelFirst()
    })

    r.subscribe(counter, () => {
      second++
    })

    r.set(counter, 1)

    expect(first).toEqual(1)
    expect(second).toEqual(1)
  })

  it("searchParam with schema reads initial query value", () => {
    const previousWindow = (globalThis as any).window
    const r = AtomRegistry.make()
    Object.defineProperty(globalThis, "window", {
      value: {
        location: {
          pathname: "/",
          search: "?page=6"
        },
        history: {
          pushState: () => {
          }
        },
        addEventListener: () => {
        },
        removeEventListener: () => {
        }
      },
      configurable: true,
      writable: true
    })

    try {
      const page = Atom.searchParam("page", { schema: Schema.NumberFromString })
      expect(r.get(page)).toEqual(Option.some(6))
    } finally {
      r.dispose()
      if (typeof previousWindow === "undefined") {
        delete (globalThis as any).window
      } else {
        Object.defineProperty(globalThis, "window", {
          value: previousWindow,
          configurable: true,
          writable: true
        })
      }
    }
  })

  it("runtime", async () => {
    const count = counterRuntime.atom(Counter.use((_) => _.get)).pipe(
      Atom.withLabel("count")
    )
    const r = AtomRegistry.make()
    const result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(1)
  })

  it("runtime replacement", async () => {
    const count = counterRuntime.atom(Counter.use((_) => _.get))
    const r = AtomRegistry.make({
      initialValues: [Atom.initialValue(counterRuntime.layer, CounterTest)]
    })
    const result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(10)
  })

  it("runtime replacement", async () => {
    const count = Atom.fnSync<number, number>((x) => x, { initialValue: 0 })
    const r = AtomRegistry.make({ initialValues: [Atom.initialValue(count, 10)] })
    const result = r.get(count)
    expect(result).toEqual(10)
    r.set(count, 20)
    const result2 = r.get(count)
    expect(result2).toEqual(20)
  })

  it("runtime multiple", async () => {
    const buildCount = buildCounterRuntime.fn<void>()((_) => BuildCounter.use((_) => _.get))
    const count = counterRuntime.atom(Counter.use((_) => _.get))
    const timesTwo = multiplierRuntime.atom((get) =>
      Effect.gen(function*() {
        const counter = yield* Counter
        const multiplier = yield* Multiplier
        yield* counter.inc
        expect(yield* get.result(count)).toEqual(2)
        return yield* multiplier.times(2)
      })
    )
    const r = AtomRegistry.make()
    const cancel = r.mount(buildCount)

    let result = r.get(timesTwo)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(4)

    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(2)

    r.set(buildCount, void 0)
    assert.deepStrictEqual(r.get(buildCount), AsyncResult.success(1))

    await Effect.runPromise(Effect.yieldNow)
    await Effect.runPromise(Effect.yieldNow)

    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(1)

    r.set(buildCount, void 0)
    assert.deepStrictEqual(r.get(buildCount), AsyncResult.success(2))

    cancel()
  })

  it("runtime direct tag", async () => {
    const counter = counterRuntime.atom(Counter)
    const r = AtomRegistry.make()
    const result = r.get(counter)
    assert(AsyncResult.isSuccess(result))
    assert(Effect.isEffect(result.value.get))
  })

  it("effect initial", async () => {
    const count = Atom.make(
      Effect.succeed(1).pipe(Effect.delay(100)),
      { initialValue: 0 }
    ).pipe(Atom.keepAlive)
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)

    await vitest.advanceTimersByTimeAsync(100)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(1)
  })

  it("effectFn", async () => {
    const count = Atom.fn((n: number) => Effect.succeed(n + 1))
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isInitial(result))
    r.set(count, 1)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(2)
  })

  it("effectFn concurrent", async () => {
    const latches = Arr.empty<Latch.Latch>()
    let done = 0
    const count = Atom.fn((_: number) => {
      const latch = Latch.makeUnsafe()
      latches.push(latch)
      return latch.await.pipe(
        Effect.tap(() => Effect.sync(() => done++))
      )
    }, { concurrent: true })

    const r = AtomRegistry.make()
    r.mount(count)

    let result = r.get(count)
    assert(AsyncResult.isInitial(result))

    r.set(count, 1)
    result = r.get(count)
    assert(AsyncResult.isInitial(result) && result.waiting)

    r.set(count, 1)
    r.set(count, 1)
    assert(AsyncResult.isInitial(result) && result.waiting)
    assert.strictEqual(latches.length, 3)
    assert.strictEqual(done, 0)

    latches.forEach((latch) => latch.openUnsafe())
    await Effect.runPromise(Effect.yieldNow)
    assert.strictEqual(done, 3)

    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
  })

  it("effectFn initial", async () => {
    const count = Atom.fn((n: number) => Effect.succeed(n + 1), {
      initialValue: 0
    })
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)
    r.set(count, 1)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(2)
  })

  it("effect mapResult", async () => {
    const count = Atom.fn((n: number) => Effect.succeed(n + 1)).pipe(
      Atom.mapResult((_) => _ + 1)
    )
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isInitial(result))
    r.set(count, 1)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(3)
  })

  it("effect double mapResult", async () => {
    const seed = Atom.make(0)
    const count = Atom.make((get) => Effect.succeed(get(seed) + 1)).pipe(
      Atom.mapResult((_) => _ + 10),
      Atom.mapResult((_) => _ + 100)
    )
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(111)
    r.set(seed, 1)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(112)
  })

  it("effect double mapResult refresh", async () => {
    let rebuilds = 0
    const count = Atom.make(() => {
      rebuilds++
      return Effect.succeed(1)
    }).pipe(
      Atom.mapResult((_) => _ + 10),
      Atom.mapResult((_) => _ + 100)
    )
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(111)
    expect(rebuilds).toEqual(1)
    r.refresh(count)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(111)
    expect(rebuilds).toEqual(2)
  })

  it("keeps parent child links when a parent is read more than once", () => {
    const flag = Atom.make(true)
    const base = Atom.make(0)
    const derived = Atom.make((get) => {
      const value = get(base)
      if (get(flag)) {
        get(base)
      }
      return value
    })
    const registry = AtomRegistry.make()
    const unsubscribe = registry.subscribe(derived, () => {
    }, { immediate: true })
    const nodes = registry.getNodes()
    const baseNode = nodes.get(base)
    const derivedNode = nodes.get(derived)

    assert(baseNode !== undefined)
    assert(derivedNode !== undefined)
    assert.strictEqual(baseNode.children.has(derivedNode), true)
    assert.strictEqual(derivedNode.parents.has(baseNode), true)

    registry.set(flag, false)

    assert.strictEqual(baseNode.children.has(derivedNode), true)
    assert.strictEqual(derivedNode.parents.has(baseNode), true)

    registry.set(base, 1)

    assert.strictEqual(registry.get(derived), 1)
    unsubscribe()
  })

  it("refresh derived before mount resolves base effect", async () => {
    const baseAtom = Atom.make(
      Effect.succeed("value").pipe(Effect.delay(100))
    )
    const derivedAtom = Atom.writable(
      (get) => get(baseAtom),
      () => {
      },
      (refresh) => refresh(baseAtom)
    )
    const registry = AtomRegistry.make()

    registry.refresh(derivedAtom)
    const unmount = registry.mount(derivedAtom)

    let result = registry.get(derivedAtom)
    assert(result.waiting)

    await vitest.advanceTimersByTimeAsync(100)

    result = registry.get(derivedAtom)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual("value")

    unmount()
  })

  it("scopedFn", async () => {
    let finalized = 0
    const count = Atom.fn((n: number) =>
      Effect.succeed(n + 1).pipe(
        Effect.tap(
          Effect.addFinalizer(() =>
            Effect.sync(() => {
              finalized++
            })
          )
        )
      )
    ).pipe(Atom.keepAlive)
    const r = AtomRegistry.make()
    let result = r.get(count)
    assert(AsyncResult.isInitial(result))

    await new Promise((resolve) => resolve(null))
    expect(finalized).toEqual(0)

    r.set(count, 1)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(2)

    r.set(count, 2)
    await new Promise((resolve) => resolve(null))
    expect(finalized).toEqual(1)
  })

  it("disposed lifetime apis are no-ops", () => {
    let context: Atom.AtomContext | undefined
    const state = Atom.make(0).pipe(Atom.keepAlive)
    const option = Atom.make<Option.Option<number>>(Option.some(1)).pipe(Atom.keepAlive)
    const result = Atom.make<AsyncResult.AsyncResult<number, never>>(AsyncResult.success(1)).pipe(Atom.keepAlive)
    const atom = Atom.make((get) => {
      context = get
      return get(state)
    }).pipe(Atom.keepAlive)
    const registry = AtomRegistry.make()

    expect(registry.get(atom)).toEqual(0)
    registry.refresh(atom)

    assert(context !== undefined)
    const disposed = context

    expect(() =>
      disposed.addFinalizer(() => {
      })
    ).not.toThrow()
    expect(disposed(state)).toEqual(0)
    expect(disposed.get(state)).toEqual(0)
    expect(disposed.once(state)).toEqual(0)
    expect(disposed.self<number>()).toEqual(Option.none())
    expect(() => disposed.result(result)).not.toThrow()
    expect(() => disposed.resultOnce(result)).not.toThrow()
    expect(() => disposed.setResult(result, AsyncResult.success(2))).not.toThrow()
    expect(() => disposed.some(option)).not.toThrow()
    expect(() => disposed.someOnce(option)).not.toThrow()
    expect(() => disposed.refresh(state)).not.toThrow()
    expect(() => disposed.refreshSelf()).not.toThrow()
    expect(() => disposed.mount(state)).not.toThrow()
    expect(() =>
      disposed.subscribe(state, () => {
      })
    ).not.toThrow()
    expect(() => disposed.setSelf(1)).not.toThrow()
    expect(() => disposed.set(state, 1)).not.toThrow()
    expect(registry.get(state)).toEqual(0)
    expect(() => disposed.stream(state)).not.toThrow()
    expect(() => disposed.streamResult(result)).not.toThrow()
  })

  it("disposed lifetime ignores async updates", async () => {
    const count = Atom.make(
      Effect.succeed(1).pipe(Effect.delay(100)),
      {
        initialValue: 0,
        uninterruptible: true
      }
    )
    const registry = AtomRegistry.make()
    const unmount = registry.mount(count)

    const initial = registry.get(count)
    assert(AsyncResult.isSuccess(initial))
    expect(initial.waiting).toEqual(true)
    expect(initial.value).toEqual(0)

    unmount()
    await vitest.advanceTimersByTimeAsync(100)
    await Effect.runPromise(Effect.yieldNow)

    const next = registry.get(count)
    assert(AsyncResult.isSuccess(next))
    expect(next.waiting).toEqual(true)
  })

  it.effect("stream", () =>
    Effect.gen(function*() {
      vitest.useRealTimers()

      const services = yield* Effect.context<never>()
      const count = Atom.make(
        Stream.range(0, 2).pipe(
          Stream.tap(() => AtomRegistry.AtomRegistry),
          Stream.tap((_) => Effect.sleep(50)),
          Stream.provideContext(services)
        )
      )
      const r = AtomRegistry.make()
      const unmount = r.mount(count)
      let result = r.get(count)
      assert(result.waiting)
      assert(AsyncResult.isInitial(result))

      yield* TestClock.adjust(50)
      result = r.get(count)
      assert(result.waiting)
      assert(AsyncResult.isSuccess(result))
      assert.deepEqual(result.value, 0)

      yield* TestClock.adjust(50)
      result = r.get(count)
      assert(result.waiting)
      assert(AsyncResult.isSuccess(result))
      assert.deepEqual(result.value, 1)

      yield* TestClock.adjust(50)
      result = r.get(count)
      assert(!result.waiting)
      assert(AsyncResult.isSuccess(result))
      assert.deepEqual(result.value, 2)

      unmount()
      yield* TestClock.adjust(50)
      result = r.get(count)
      assert(result.waiting)
      assert(AsyncResult.isInitial(result))
    }))

  it("stream initial", async () => {
    const count = Atom.make(
      Stream.range(1, 2).pipe(
        Stream.tap(() => Effect.sleep(50))
      ),
      { initialValue: 0 }
    )
    const r = AtomRegistry.make()
    const unmount = r.mount(count)
    let result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, 1)

    unmount()
    await new Promise((resolve) => resolve(null))
    result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isSuccess(result))
  })

  it("streamFn", async () => {
    const count = Atom.fn((start: number) =>
      Stream.range(start, start + 1).pipe(
        Stream.tap(() => Effect.sleep(50))
      )
    )
    const r = AtomRegistry.make()
    const unmount = r.mount(count)
    let result = r.get(count)
    assert.strictEqual(result._tag, "Initial")

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert.strictEqual(result._tag, "Initial")

    r.set(count, 1)
    result = r.get(count)
    assert(result.waiting)
    assert.strictEqual(result._tag, "Initial")

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, 1)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, 2)

    r.set(count, 5)
    result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, 2)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, 5)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, 6)

    unmount()
    await Effect.runPromise(Effect.yieldNow)
    result = r.get(count)
    assert(AsyncResult.isInitial(result))
  })

  it("pull", async () => {
    const count = Atom.pull(
      Stream.range(0, 1, 1).pipe(
        Stream.tap(() => Effect.sleep(50))
      )
    )
    const r = AtomRegistry.make()
    const unmount = r.mount(count)

    let result = r.get(count)
    assert(result.waiting)
    assert(Option.isNone(AsyncResult.value(result)))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [0] })

    r.set(count, void 0)
    result = r.get(count)
    assert(result.waiting)
    assert.deepEqual(AsyncResult.value(result), Option.some({ done: false, items: [0] }))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [0, 1] })

    r.set(count, void 0)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: true, items: [0, 1] })

    r.refresh(count)
    result = r.get(count)
    assert(result.waiting)
    assert.deepEqual(AsyncResult.value(result), Option.some({ done: true, items: [0, 1] }))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [0] })

    unmount()
    await Effect.runPromise(Effect.yieldNow)
    result = r.get(count)
    assert(result.waiting)
    assert(Option.isNone(AsyncResult.value(result)))
  })

  it("pull runtime", async () => {
    const count = counterRuntime.pull(
      Counter.use((_) => _.get).pipe(
        Effect.map((_) => Stream.range(_, 2, 1)),
        Stream.unwrap,
        Stream.tap(() => Effect.sleep(50))
      )
    )
    const r = AtomRegistry.make()
    const unmount = r.mount(count)

    let result = r.get(count)
    assert(result.waiting)
    assert(Option.isNone(AsyncResult.value(result)))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [1] })

    r.set(count, void 0)
    result = r.get(count)
    assert(result.waiting)
    assert.deepEqual(AsyncResult.value(result), Option.some({ done: false, items: [1] }))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [1, 2] })

    r.set(count, void 0)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: true, items: [1, 2] })

    r.refresh(count)
    result = r.get(count)
    assert(result.waiting)
    assert.deepEqual(AsyncResult.value(result), Option.some({ done: true, items: [1, 2] }))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [1] })

    unmount()
    await Effect.runPromise(Effect.yieldNow)
    result = r.get(count)
    assert(result.waiting)
    assert(Option.isNone(AsyncResult.value(result)))
  })

  it("pull refreshable", async () => {
    const count = Atom.pull(() =>
      Stream.range(1, 2, 1).pipe(
        Stream.tap(() => Effect.sleep(50))
      )
    )
    const r = AtomRegistry.make()
    const unmount = r.mount(count)

    let result = r.get(count)
    assert(result.waiting)
    assert(AsyncResult.isInitial(result))

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(count)
    assert(!result.waiting)
    assert(AsyncResult.isSuccess(result))
    assert.deepEqual(result.value, { done: false, items: [1] })

    unmount()
    await Effect.runPromise(Effect.yieldNow)
    result = r.get(count)
    assert(result.waiting)
  })

  it("family", async () => {
    const r = AtomRegistry.make()

    const count = Atom.family((n: number) => Atom.make(n))
    const hash = Hash.hash(count(1))
    assert.strictEqual(count(1), count(1))
    r.set(count(1), 2)
    assert.strictEqual(r.get(count(1)), 2)

    const countKeep = Atom.family((n: number) => Atom.make(n).pipe(Atom.keepAlive))
    assert.strictEqual(countKeep(1), countKeep(1))
    r.get(countKeep(1))
    const hashKeep = Hash.hash(countKeep(1))

    if (global.gc) {
      vitest.useRealTimers()
      await new Promise((resolve) => setTimeout(resolve, 0))
      global.gc()
      assert.notEqual(hash, Hash.hash(count(1)))
      assert.strictEqual(hashKeep, Hash.hash(countKeep(1)))
    }
  })

  it("label", async () => {
    expect(
      Atom.make(0).pipe(Atom.withLabel("counter")).label![1]
    ).toMatch(/Atom.test.ts:\d+:\d+/)
  })

  it("batching", async () => {
    const r = AtomRegistry.make()
    const state = Atom.make(1).pipe(Atom.keepAlive)
    const state2 = Atom.make("a").pipe(Atom.keepAlive)
    let count = 0
    const derived = Atom.readable((get) => {
      count++
      return get(state) + get(state2)
    })
    expect(r.get(derived)).toEqual("1a")
    expect(count).toEqual(1)
    Atom.batch(() => {
      r.set(state, 2)
      r.set(state2, "b")
    })
    expect(count).toEqual(2)
    expect(r.get(derived)).toEqual("2b")
  })

  it("nested batch", async () => {
    const r = AtomRegistry.make()
    const state = Atom.make(1).pipe(Atom.keepAlive)
    const state2 = Atom.make("a").pipe(Atom.keepAlive)
    let count = 0
    const derived = Atom.readable((get) => {
      count++
      return get(state) + get(state2)
    })
    expect(r.get(derived)).toEqual("1a")
    expect(count).toEqual(1)
    Atom.batch(() => {
      r.set(state, 2)
      Atom.batch(() => {
        r.set(state2, "b")
      })
    })
    expect(count).toEqual(2)
    expect(r.get(derived)).toEqual("2b")
  })

  it("read correct updated state in batch", async () => {
    const r = AtomRegistry.make()
    const state = Atom.make(1).pipe(Atom.keepAlive)
    const state2 = Atom.make("a").pipe(Atom.keepAlive)
    let count = 0
    const derived = Atom.readable((get) => {
      count++
      return get(state) + get(state2)
    })
    expect(r.get(derived)).toEqual("1a")
    expect(count).toEqual(1)
    Atom.batch(() => {
      r.set(state, 2)
      expect(r.get(derived)).toEqual("2a")
      r.set(state2, "b")
    })
    expect(count).toEqual(3)
    expect(r.get(derived)).toEqual("2b")
    expect(count).toEqual(3)
  })

  it("notifies listeners after batch commit", async () => {
    const r = AtomRegistry.make()
    const state = Atom.make(1).pipe(Atom.keepAlive)
    const state2 = Atom.make("a").pipe(Atom.keepAlive)
    let count = 0
    const derived = Atom.readable((get) => {
      return get(state) + get(state2)
    })
    r.subscribe(derived, () => {
      count++
    })
    Atom.batch(() => {
      r.get(derived)
      r.set(state, 2)
      r.get(derived)
      r.set(state2, "b")
    })
    expect(count).toEqual(1)
    expect(r.get(derived)).toEqual("2b")
  })

  it("initialValues", async () => {
    const state = Atom.make(0)
    const r = AtomRegistry.make({
      initialValues: [
        Atom.initialValue(state, 10)
      ]
    })
    expect(r.get(state)).toEqual(10)
    await Effect.runPromise(Effect.yieldNow)
    expect(r.get(state)).toEqual(0)
  })

  it("map with initialValue still rerenders when source changes", () => {
    const state = Atom.make(0).pipe(Atom.keepAlive)
    const mapped = state.pipe(
      Atom.map((n) => n + 1),
      Atom.keepAlive
    )
    const r = AtomRegistry.make({
      initialValues: [
        Atom.initialValue(mapped, 10)
      ]
    })
    r.mount(mapped)

    assert.strictEqual(r.get(mapped), 10)

    r.set(state, 1)

    assert.strictEqual(r.get(mapped), 2)
  })

  it("idleTTL", async () => {
    const state = Atom.make(0)
    const state2 = Atom.make(0).pipe(
      Atom.setIdleTTL(10000)
    )
    const state3 = Atom.make(0).pipe(
      Atom.setIdleTTL(3000)
    )
    const r = AtomRegistry.make({ defaultIdleTTL: 2000 })
    r.set(state, 10)
    r.set(state2, 10)
    r.set(state3, 10)
    expect(r.get(state)).toEqual(10)
    expect(r.get(state2)).toEqual(10)
    expect(r.get(state3)).toEqual(10)
    await new Promise((resolve) => resolve(null))
    expect(r.get(state)).toEqual(10)
    expect(r.get(state2)).toEqual(10)
    expect(r.get(state3)).toEqual(10)

    await new Promise((resolve) => resolve(null))
    await vitest.advanceTimersByTimeAsync(10000)
    expect(r.get(state)).toEqual(0)
    expect(r.get(state2)).toEqual(10)
    expect(r.get(state3)).toEqual(0)

    await new Promise((resolve) => resolve(null))
    await vitest.advanceTimersByTimeAsync(20000)
    expect(r.get(state)).toEqual(0)
    expect(r.get(state2)).toEqual(0)
    expect(r.get(state3)).toEqual(0)
  })

  it("idleTTL fn", async () => {
    const fn = Atom.fn((n: number) => Effect.succeed(n + 1)).pipe(
      Atom.setIdleTTL(0)
    )
    const r = AtomRegistry.make({ defaultIdleTTL: 2000 })

    let result = r.get(fn)
    assert(AsyncResult.isInitial(result))

    r.set(fn, 1)
    result = r.get(fn)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(2)

    await Effect.runPromise(Effect.yieldNow)

    result = r.get(fn)
    assert(AsyncResult.isInitial(result))
  })

  it("idleTTL fnSync", async () => {
    const fn = Atom.fnSync((n: number) => n + 1).pipe(
      Atom.setIdleTTL(0)
    )
    const r = AtomRegistry.make({ defaultIdleTTL: 2000 })

    let result = r.get(fn)
    assert(Option.isNone(result))

    r.set(fn, 1)
    result = r.get(fn)
    assert(Option.isSome(result))
    expect(result.value).toEqual(2)

    await Effect.runPromise(Effect.yieldNow)

    result = r.get(fn)
    assert(Option.isNone(result))
  })

  it("fn", async () => {
    const count = Atom.fnSync((n: number) => n).pipe(Atom.keepAlive)
    const r = AtomRegistry.make()
    assert.deepEqual(r.get(count), Option.none())

    r.set(count, 1)
    assert.deepEqual(r.get(count), Option.some(1))
  })

  it("fn initial", async () => {
    const count = Atom.fnSync((n: number) => n, { initialValue: 0 })
    const r = AtomRegistry.make()
    assert.deepEqual(r.get(count), 0)

    r.set(count, 1)
    assert.deepEqual(r.get(count), 1)
  })

  it("withFallback", async () => {
    const count = Atom.make(() =>
      Effect.succeed(1).pipe(
        Effect.delay(100)
      )
    ).pipe(
      Atom.withFallback(Atom.make(() => Effect.succeed(0))),
      Atom.keepAlive
    )
    const r = AtomRegistry.make()
    assert.deepEqual(r.get(count), AsyncResult.waiting(AsyncResult.success(0)))

    await vitest.advanceTimersByTimeAsync(100)
    assert.deepEqual(r.get(count), AsyncResult.success(1))
  })

  it("failure with previousSuccess", async () => {
    const count = Atom.fn((i: number) => i === 1 ? Effect.fail("fail") : Effect.succeed(i))
    const r = AtomRegistry.make()

    let result = r.get(count)
    assert(AsyncResult.isInitial(result))

    r.set(count, 0)
    result = r.get(count)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)

    r.set(count, 1)
    result = r.get(count)
    assert(AsyncResult.isFailure(result))
    const error = Cause.findErrorOption(result.cause)
    assert(Option.isSome(error))
    assert.strictEqual(error.value, "fail")

    const value = AsyncResult.value(result)
    assert(Option.isSome(value))
    assert.strictEqual(value.value, 0)
  })

  it("read non-object", () => {
    const bool = Atom.make(() => true)
    const r = AtomRegistry.make()
    assert.strictEqual(r.get(bool), true)
  })

  it("get.stream", async () => {
    const count = Atom.make(0)
    const multiplied = Atom.make((get) => get.stream(count).pipe(Stream.map((_) => _ * 2)))

    const r = AtomRegistry.make()
    const cancel = r.mount(multiplied)

    assert.strictEqual(r.get(count), 0)
    assert.deepStrictEqual(r.get(multiplied), AsyncResult.success(0, { waiting: true }))

    r.set(count, 1)
    await Effect.runPromise(Effect.yieldNow)
    assert.deepStrictEqual(r.get(multiplied), AsyncResult.success(2, { waiting: true }))

    cancel()
  })

  it("get.streamResult", async () => {
    const count = Atom.make(0)
    const multiplied = Atom.make((get) => get.stream(count).pipe(Stream.map((_) => _ * 2)))
    const plusOne = Atom.make((get) => get.streamResult(multiplied).pipe(Stream.map((_) => _ + 1)))

    const r = AtomRegistry.make()
    const cancel = r.mount(plusOne)

    assert.strictEqual(r.get(count), 0)
    assert.deepStrictEqual(r.get(plusOne), AsyncResult.success(1, { waiting: true }))

    r.set(count, 1)
    await Effect.runPromise(Effect.yieldNow)
    await Effect.runPromise(Effect.yieldNow)
    assert.deepStrictEqual(r.get(plusOne), AsyncResult.success(3, { waiting: true }))

    cancel()
  })

  it("stream failure keeps previousSuccess", async () => {
    const atom = Atom.make(() => Stream.succeed(1).pipe(Stream.concat(Stream.fail("boom"))))
    const r = AtomRegistry.make()
    const cancel = r.mount(atom)

    await new Promise((resolve) => resolve(null))
    const afterFail = r.get(atom)
    assert(AsyncResult.isFailure(afterFail))
    const prev = AsyncResult.value(afterFail)
    assert(Option.isSome(prev))
    assert.strictEqual(prev.value, 1)

    cancel()
  })

  it("stream empty produces NoSuchElementError", async () => {
    const atom = Atom.make(Stream.empty) satisfies Atom.Atom<
      AsyncResult.AsyncResult<never, Cause.NoSuchElementError>
    >
    const r = AtomRegistry.make()
    const cancel = r.mount(atom)

    await vitest.advanceTimersByTimeAsync(0)
    const result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.deepStrictEqual(
      AsyncResult.error(result),
      Option.some(new Cause.NoSuchElementError())
    )

    cancel()
  })

  it("Option is not an Effect", async () => {
    const atom = Atom.make(Option.none<string>())
    const r = AtomRegistry.make()
    assert.deepStrictEqual(r.get(atom), Option.none())
  })

  it("Either is not an Effect", async () => {
    const atom = Atom.make(Result.succeed(123))
    const r = AtomRegistry.make()
    assert.deepStrictEqual(r.get(atom), Result.succeed(123))
  })

  it("SubscriptionRef", async () => {
    vitest.useRealTimers()
    const ref = SubscriptionRef.make(123).pipe(Effect.runSync)
    const atom = Atom.subscriptionRef(ref)
    const r = AtomRegistry.make()
    assert.deepStrictEqual(r.get(atom), 123)
    await Effect.runPromise(SubscriptionRef.update(ref, (a) => a + 1))
    assert.deepStrictEqual(r.get(atom), 124)
  })

  it("SubscriptionRef", async () => {
    vitest.useRealTimers()
    const ref = SubscriptionRef.make(0).pipe(Effect.runSync)
    const atom = Atom.subscriptionRef(ref)
    const r = AtomRegistry.make()
    const unmount = r.mount(atom)
    assert.deepStrictEqual(r.get(atom), 0)
    r.set(atom, 1)
    await new Promise((resolve) => resolve(null))
    assert.deepStrictEqual(r.get(atom), 1)
    unmount()
  })

  it("SubscriptionRef/effect", async () => {
    const atom = Atom.subscriptionRef(SubscriptionRef.make(0))
    const r = AtomRegistry.make()
    const unmount = r.mount(atom)
    assert.deepStrictEqual(r.get(atom), AsyncResult.success(0, { waiting: true }))
    r.set(atom, 1)
    await new Promise((resolve) => resolve(null))
    assert.deepStrictEqual(r.get(atom), AsyncResult.success(1, { waiting: true }))
    unmount()
  })

  it("SubscriptionRef/runtime", async () => {
    const atom = counterRuntime.subscriptionRef(SubscriptionRef.make(0))
    const r = AtomRegistry.make()
    const unmount = r.mount(atom)
    assert.deepStrictEqual(r.get(atom), AsyncResult.success(0, { waiting: true }))
    r.set(atom, 1)
    await new Promise((resolve) => resolve(null))
    assert.deepStrictEqual(r.get(atom), AsyncResult.success(1, { waiting: true }))
    unmount()
  })

  it("SubscriptionRef/runtime/scoped", async () => {
    let finalized = false
    const atom = counterRuntime.subscriptionRef(
      Effect.gen(function*() {
        yield* Effect.addFinalizer(() =>
          Effect.sync(() => {
            finalized = true
          })
        )
        return yield* SubscriptionRef.make(0)
      })
    )
    const r = AtomRegistry.make()
    const unmount = r.mount(atom)
    assert.deepStrictEqual(r.get(atom), AsyncResult.success(0, { waiting: true }))
    r.set(atom, 1)
    await new Promise((resolve) => resolve(null))
    assert.deepStrictEqual(r.get(atom), AsyncResult.success(1, { waiting: true }))
    assert.strictEqual(finalized, false)
    unmount()
    await Effect.runPromise(Effect.yieldNow)
    assert.strictEqual(finalized, true)
  })

  it("setLazy(true)", async () => {
    const count = Atom.make(0).pipe(Atom.keepAlive)
    let rebuilds = 0
    const double = Atom.make((get) => {
      rebuilds++
      return get(count) * 2
    }).pipe(Atom.keepAlive)
    const r = AtomRegistry.make()
    assert.strictEqual(r.get(double), 0)
    r.set(count, 1)
    assert.strictEqual(rebuilds, 1)
    assert.strictEqual(r.get(double), 2)
    assert.strictEqual(rebuilds, 2)
  })

  it("setLazy(false)", async () => {
    const count = Atom.make(0).pipe(Atom.keepAlive)
    let rebuilds = 0
    const double = Atom.make((get) => {
      rebuilds++
      return get(count) * 2
    }).pipe(Atom.setLazy(false), Atom.keepAlive)
    const r = AtomRegistry.make()
    assert.strictEqual(r.get(double), 0)
    r.set(count, 1)
    assert.strictEqual(rebuilds, 2)
    assert.strictEqual(r.get(double), 2)
    assert.strictEqual(rebuilds, 2)
  })

  it("derived derived with with effect result", async () => {
    const r = AtomRegistry.make()
    const state = Atom.fn(Effect.succeed<number>)
    let count = 0
    const derived = Atom.readable((get) => {
      count++
      return get(state).pipe(AsyncResult.getOrElse(() => -1)) % 3
    })
    let count2 = 0
    const derived2 = Atom.readable((get) => {
      count2++
      return get(derived) + 10
    })
    const cancel = r.mount(derived2)

    expect(r.get(derived)).toEqual(-1)
    expect(count).toEqual(1)
    expect(r.get(derived2)).toEqual(9)
    expect(count2).toEqual(1)
    r.set(state, 2)
    expect(r.get(derived)).toEqual(2)
    expect(count).toEqual(2)
    expect(r.get(derived2)).toEqual(12)
    expect(count2).toEqual(2)
    r.set(state, 5)
    expect(r.get(derived)).toEqual(2)
    expect(count).toEqual(3)
    expect(r.get(derived2)).toEqual(12)
    expect(count2).toEqual(2)
    cancel()
  })

  test(`toStreamResult`, async () => {
    const r = AtomRegistry.make()
    const atom = Atom.make(Effect.succeed(1))
    const eff = Atom.toStreamResult(atom).pipe(
      Stream.runHead,
      Effect.provideService(AtomRegistry.AtomRegistry, r)
    )
    const result = await Effect.runPromise(eff)
    expect(Option.getOrThrow(result)).toEqual(1)
  })

  test(`refreshOnSignal`, async () => {
    const r = AtomRegistry.make()
    let rebuilds = 0
    const signal = Atom.make(0)
    const refreshOnSignal = Atom.makeRefreshOnSignal(signal)
    const atom = Atom.make(() => {
      rebuilds++
      return 123
    }).pipe(refreshOnSignal)
    r.mount(atom)

    assert.strictEqual(r.get(atom), 123)
    assert.strictEqual(rebuilds, 1)
    r.get(atom)
    assert.strictEqual(rebuilds, 1)

    r.set(signal, 1)
    assert.strictEqual(rebuilds, 2)
  })

  test(`refreshOnSignal uses registry initial values as source state`, () => {
    let rebuilds = 0
    let value = 0
    const signal = Atom.make(0)
    const source = Atom.make(() => {
      rebuilds++
      return value
    }).pipe(Atom.keepAlive)
    const atom = source.pipe(
      Atom.makeRefreshOnSignal(signal),
      Atom.keepAlive
    )
    const r = AtomRegistry.make({ initialValues: [Atom.initialValue(atom, 10)] })
    r.mount(atom)

    assert.strictEqual(r.get(atom), 10)
    assert.strictEqual(r.get(source), 10)
    assert.strictEqual(rebuilds, 1)

    value = 11
    r.set(signal, 1)

    assert.strictEqual(r.get(atom), 11)
    assert.strictEqual(r.get(source), 11)
    assert.strictEqual(rebuilds, 2)
  })

  test(`debounce uses registry initial values as source state`, async () => {
    const source = Atom.make(0).pipe(Atom.keepAlive)
    const atom = source.pipe(
      Atom.debounce(100),
      Atom.keepAlive
    )
    const r = AtomRegistry.make({ initialValues: [Atom.initialValue(atom, 10)] })
    r.mount(atom)

    assert.strictEqual(r.get(atom), 10)
    assert.strictEqual(r.get(source), 10)

    r.set(source, 11)
    assert.strictEqual(r.get(atom), 10)

    await vitest.advanceTimersByTimeAsync(100)

    assert.strictEqual(r.get(atom), 11)
    assert.strictEqual(r.get(source), 11)
  })

  test(`withRefresh uses registry initial values as source state`, async () => {
    let rebuilds = 0
    let value = 0
    const source = Atom.make(() => {
      rebuilds++
      return value
    }).pipe(Atom.keepAlive)
    const atom = source.pipe(
      Atom.withRefresh(100),
      Atom.keepAlive
    )
    const r = AtomRegistry.make({ initialValues: [Atom.initialValue(atom, 10)] })
    r.mount(atom)

    assert.strictEqual(r.get(atom), 10)
    assert.strictEqual(r.get(source), 10)
    assert.strictEqual(rebuilds, 1)

    value = 11
    await vitest.advanceTimersByTimeAsync(100)

    assert.strictEqual(r.get(atom), 11)
    assert.strictEqual(r.get(source), 11)
    assert.strictEqual(rebuilds, 2)
  })

  test(`swr refresh is forceful while fresh`, () => {
    const r = AtomRegistry.make()
    let runs = 0
    const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
      Atom.swr({ staleTime: 1_000 })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    r.refresh(atom)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr keeps previous value while stale revalidation runs`, async () => {
    const r = AtomRegistry.make()
    let runs = 0
    const atom = Atom.make(Effect.sync(() => ++runs).pipe(Effect.delay(50))).pipe(
      Atom.swr({ staleTime: 100 })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(result.waiting)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    await vitest.advanceTimersByTimeAsync(101)
    r.refresh(atom)
    result = r.get(atom)
    assert(result.waiting)
    assert.deepEqual(AsyncResult.value(result), Option.some(1))
    assert.strictEqual(runs, 1)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr refresh is forceful after failure with previousSuccess`, () => {
    const r = AtomRegistry.make()
    let runs = 0
    const atom = Atom.fn((i: number) => {
      runs++
      return i === 1 ? Effect.fail("fail") : Effect.succeed(i)
    }).pipe(
      Atom.swr({ staleTime: 1_000 })
    )
    const unmount = r.mount(atom)

    r.set(atom, 0)
    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)
    assert.strictEqual(runs, 1)

    r.set(atom, 1)
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 2)

    r.refresh(atom)
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 3)

    r.refresh(atom)
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 4)

    unmount()
  })

  test(`swr refreshes failure without previousSuccess`, () => {
    const r = AtomRegistry.make()
    let runs = 0
    const atom = Atom.make(() => {
      runs++
      return Effect.fail("fail")
    }).pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnMount: false })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 1)

    r.refresh(atom)
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr revalidateOnMount false skips first read only`, () => {
    const r = AtomRegistry.make()
    const focusSignal = Atom.make(0)
    let focus = 0
    const emitFocus = () => r.set(focusSignal, ++focus)
    let runs = 0
    const atom = Atom.make(() => {
      runs++
      return Effect.fail("fail")
    }).pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnMount: false, revalidateOnFocus: true, focusSignal })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 1)

    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr auto revalidates failure with previousSuccess only when stale`, async () => {
    const r = AtomRegistry.make()
    const focusSignal = Atom.make(0)
    let focus = 0
    const emitFocus = () => r.set(focusSignal, ++focus)
    let runs = 0
    const atom = Atom.fn((i: number) => {
      runs++
      return i === 0 ? Effect.succeed(i) : Effect.fail("fail")
    }).pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnMount: false, revalidateOnFocus: true, focusSignal })
    )
    const unmount = r.mount(atom)

    r.set(atom, 0)
    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)
    assert.strictEqual(runs, 1)

    r.set(atom, 1)
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 2)

    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 2)

    await vitest.advanceTimersByTimeAsync(1_001)
    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isFailure(result))
    assert.strictEqual(runs, 3)

    unmount()
  })

  test(`swr does not refresh from initial state`, () => {
    const r = AtomRegistry.make()
    let runs = 0
    const atom = Atom.fn((n: number) => {
      runs++
      return Effect.succeed(n)
    }).pipe(
      Atom.swr({ staleTime: 0 })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isInitial(result))
    assert.strictEqual(result.waiting, false)
    assert.strictEqual(runs, 0)

    r.refresh(atom)
    result = r.get(atom)
    assert(AsyncResult.isInitial(result))
    assert.strictEqual(result.waiting, false)
    assert.strictEqual(runs, 0)

    unmount()
  })

  test(`swr refresh is forceful while waiting`, async () => {
    const r = AtomRegistry.make()
    let runs = 0
    const atom = Atom.make(Effect.sync(() => ++runs).pipe(Effect.tap(() => Effect.sleep(50)))).pipe(
      Atom.swr({ staleTime: 0 })
    )
    const unmount = r.mount(atom)

    await vitest.advanceTimersByTimeAsync(50)
    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    r.refresh(atom)
    result = r.get(atom)
    assert(result.waiting)
    assert.strictEqual(runs, 2)

    r.refresh(atom)
    result = r.get(atom)
    assert(result.waiting)
    assert.strictEqual(runs, 3)

    await vitest.advanceTimersByTimeAsync(50)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 3)

    unmount()
  })

  test(`swr delegates refresh to wrapped custom refresh`, () => {
    const r = AtomRegistry.make()
    let runs = 0
    const source = Atom.make(Effect.sync(() => ++runs))
    const proxy = Atom.writable<AsyncResult.AsyncResult<number, never>, void>(
      (get) => get(source),
      () => {
      },
      (refresh) => refresh(source)
    )
    const atom = proxy.pipe(Atom.swr({ staleTime: 1_000, revalidateOnMount: false }))
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    r.refresh(atom)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr revalidates on stale remount when enabled`, async () => {
    const r = AtomRegistry.make()
    let runs = 0
    const base = Atom.make(Effect.sync(() => ++runs)).pipe(Atom.keepAlive)
    const atom = base.pipe(Atom.swr({ staleTime: 100, revalidateOnMount: true }))

    const unmount1 = r.mount(atom)
    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)
    unmount1()

    await Effect.runPromise(Effect.yieldNow)
    await vitest.advanceTimersByTimeAsync(101)

    const unmount2 = r.mount(atom)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)
    unmount2()
  })

  test(`swr does not revalidate on fresh remount when enabled`, async () => {
    const r = AtomRegistry.make()
    let runs = 0
    const base = Atom.make(Effect.sync(() => ++runs)).pipe(Atom.keepAlive)
    const atom = base.pipe(Atom.swr({ staleTime: 10_000, revalidateOnMount: true }))

    const unmount1 = r.mount(atom)
    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)
    unmount1()

    await Effect.runPromise(Effect.yieldNow)

    const unmount2 = r.mount(atom)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)
    unmount2()
  })

  test(`swr revalidates on focus signal only when stale`, async () => {
    const r = AtomRegistry.make()
    const focusSignal = Atom.make(0)
    let focus = 0
    const emitFocus = () => r.set(focusSignal, ++focus)
    let runs = 0
    const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnFocus: true, focusSignal })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    await vitest.advanceTimersByTimeAsync(1_001)
    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr can force refresh on focus signal`, () => {
    const r = AtomRegistry.make()
    const focusSignal = Atom.make(0)
    let focus = 0
    const emitFocus = () => r.set(focusSignal, ++focus)
    let runs = 0
    const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnFocus: "always", focusSignal })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr treats value as stale at exact staleTime boundary`, async () => {
    const r = AtomRegistry.make()
    const focusSignal = Atom.make(0)
    let focus = 0
    const emitFocus = () => r.set(focusSignal, ++focus)
    let runs = 0
    const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnFocus: true, focusSignal })
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    await vitest.advanceTimersByTimeAsync(1_000)
    emitFocus()
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    unmount()
  })

  test(`swr does not refresh on focus signal when disabled or omitted`, () => {
    {
      const r = AtomRegistry.make()
      const focusSignal = Atom.make(0)
      let focus = 0
      const emitFocus = () => r.set(focusSignal, ++focus)
      let runs = 0
      const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
        Atom.swr({ staleTime: 10_000, revalidateOnFocus: false, focusSignal })
      )
      const unmount = r.mount(atom)

      let result = r.get(atom)
      assert(AsyncResult.isSuccess(result))
      assert.strictEqual(result.value, 1)
      assert.strictEqual(runs, 1)

      emitFocus()
      result = r.get(atom)
      assert(AsyncResult.isSuccess(result))
      assert.strictEqual(result.value, 1)
      assert.strictEqual(runs, 1)

      unmount()
    }

    {
      const r = AtomRegistry.make()
      const focusSignal = Atom.make(0)
      let focus = 0
      const emitFocus = () => r.set(focusSignal, ++focus)
      let runs = 0
      const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
        Atom.swr({ staleTime: 10_000, focusSignal })
      )
      const unmount = r.mount(atom)

      let result = r.get(atom)
      assert(AsyncResult.isSuccess(result))
      assert.strictEqual(result.value, 1)
      assert.strictEqual(runs, 1)

      emitFocus()
      result = r.get(atom)
      assert(AsyncResult.isSuccess(result))
      assert.strictEqual(result.value, 1)
      assert.strictEqual(runs, 1)

      unmount()
    }
  })

  test(`swr does not revalidate on stale remount when disabled`, async () => {
    const r = AtomRegistry.make()
    let runs = 0
    const base = Atom.make(Effect.sync(() => ++runs)).pipe(Atom.keepAlive)
    const atom = base.pipe(Atom.swr({ staleTime: 100, revalidateOnMount: false }))

    const unmount1 = r.mount(atom)
    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)
    unmount1()

    await Effect.runPromise(Effect.yieldNow)
    await vitest.advanceTimersByTimeAsync(101)

    const unmount2 = r.mount(atom)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)
    unmount2()
  })

  test(`swr composes with signal driven refresh wrappers`, async () => {
    const r = AtomRegistry.make()
    let runs = 0
    const signal = Atom.make(0)
    const atom = Atom.make(Effect.sync(() => ++runs)).pipe(
      Atom.swr({ staleTime: 1_000 }),
      Atom.makeRefreshOnSignal(signal)
    )
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(runs, 1)

    r.set(signal, 1)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)

    r.set(signal, 2)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 3)
    assert.strictEqual(runs, 3)

    unmount()
  })

  test(`swr preserves writable set semantics`, () => {
    const r = AtomRegistry.make()
    let writes = 0
    const source = Atom.writable<AsyncResult.AsyncResult<number, never>, number>(
      (get) => Option.getOrElse(get.self<AsyncResult.AsyncResult<number, never>>(), () => AsyncResult.success(0)),
      (ctx, value) => {
        writes++
        ctx.setSelf(AsyncResult.success(value))
      }
    )
    const atom = source.pipe(Atom.swr({ staleTime: 1_000 }))
    const unmount = r.mount(atom)

    let result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 0)

    r.set(atom, 1)
    result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
    assert.strictEqual(writes, 1)

    const sourceResult = r.get(source)
    assert(AsyncResult.isSuccess(sourceResult))
    assert.strictEqual(sourceResult.value, 1)

    unmount()
  })

  test(`swr uses registry initial values as source state`, () => {
    let runs = 0
    const source = Atom.make(Effect.sync(() => ++runs)).pipe(Atom.keepAlive)
    const atom = source.pipe(
      Atom.swr({ staleTime: 1_000, revalidateOnMount: false }),
      Atom.keepAlive
    )
    const initial = AsyncResult.success(10)
    const r = AtomRegistry.make({ initialValues: [Atom.initialValue(atom, initial)] })
    r.mount(atom)

    assert.deepStrictEqual(r.get(atom), initial)
    assert.deepStrictEqual(r.get(source), initial)
    assert.strictEqual(runs, 1)

    r.refresh(atom)

    const result = r.get(atom)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 2)
    assert.strictEqual(runs, 2)
  })

  // it("dehydrate", async () => {
  //   const r = AtomRegistry.make()
  //   const notSerializable = Atom.make(0)
  //   r.mount(notSerializable)
  //
  //   const basicSerializable = Atom.make(0).pipe(Atom.serializable({
  //     key: "basicSerializable",
  //     schema: Schema.Number
  //   }))
  //   r.mount(basicSerializable)
  //
  //   const errored = Atom.make(Effect.fail("error")).pipe(
  //     Atom.serializable({
  //       key: "errored",
  //       schema: AsyncResult.Schema({
  //         error: Schema.String
  //       })
  //     })
  //   )
  //   r.mount(errored)
  //
  //   const success = Atom.make(Effect.succeed(123)).pipe(Atom.serializable({
  //     key: "success",
  //     schema: AsyncResult.Schema({
  //       success: Schema.Number
  //     })
  //   }))
  //   r.mount(success)
  //
  //   const { promise, resolve } = Promise.withResolvers<number>()
  //
  //   const pending = Atom.make(Effect.promise(() => promise)).pipe(Atom.serializable({
  //     key: "pending",
  //     schema: AsyncResult.Schema({
  //       success: Schema.Number
  //     })
  //   }))
  //   r.mount(pending)
  //
  //   const state = Hydration.toValues(Hydration.dehydrate(r, {
  //     encodeInitialAs: "promise"
  //   }))
  //   expect(state.map((r) => Struct.omit(r, "dehydratedAt", "resultPromise"))).toMatchInlineSnapshot(`
  //     [
  //       {
  //         "key": "basicSerializable",
  //         "value": 0,
  //         "~@effect-atom/atom/DehydratedAtom": true,
  //       },
  //       {
  //         "key": "errored",
  //         "value": {
  //           "_tag": "Failure",
  //           "cause": {
  //             "_tag": "Fail",
  //             "error": "error",
  //           },
  //           "previousSuccess": {
  //             "_tag": "None",
  //           },
  //           "waiting": false,
  //         },
  //         "~@effect-atom/atom/DehydratedAtom": true,
  //       },
  //       {
  //         "key": "success",
  //         "value": {
  //           "_tag": "Success",
  //           "timestamp": ${Date.now()},
  //           "value": 123,
  //           "waiting": false,
  //         },
  //         "~@effect-atom/atom/DehydratedAtom": true,
  //       },
  //       {
  //         "key": "pending",
  //         "value": {
  //           "_tag": "Initial",
  //           "waiting": true,
  //         },
  //         "~@effect-atom/atom/DehydratedAtom": true,
  //       },
  //     ]
  //   `)
  //
  //   expect(state.find((r) => r.key === "pending")?.resultPromise).instanceOf(Promise)
  //
  //   const r2 = AtomRegistry.make()
  //   Hydration.hydrate(r2, state)
  //
  //   expect(r2.get(notSerializable)).toEqual(0)
  //   expect(r2.get(basicSerializable)).toEqual(0)
  //   expect(r2.get(errored)).toEqual(AsyncResult.failure(Cause.fail("error")))
  //   expect(r2.get(success)).toEqual(AsyncResult.success(123))
  //   expect(r2.get(pending)).toEqual(AsyncResult.initial(true))
  //
  //   resolve(123)
  //   await expect(state.find((r) => r.key === "pending")?.resultPromise).resolves.toEqual({
  //     "_tag": "Success",
  //     "timestamp": expect.any(Number),
  //     "value": 123,
  //     "waiting": false
  //   })
  // })

  describe("optimistic", () => {
    it("non-Result", async () => {
      const latch = Latch.makeUnsafe()
      const r = AtomRegistry.make()
      let i = 0
      const atom = Atom.make(() => i)
      const optimisticAtom = atom.pipe(Atom.optimistic)
      const fn = optimisticAtom.pipe(
        Atom.optimisticFn({
          reducer: (_current, update: number) => update,
          fn: Atom.fn(Effect.fnUntraced(function*() {
            yield* latch.await
          }))
        }),
        Atom.keepAlive
      )

      expect(r.get(atom)).toEqual(0)
      expect(r.get(optimisticAtom)).toEqual(0)
      r.set(fn, 1)
      i = 2

      // optimistic phase: the optimistic value is set, but the true value is not
      expect(r.get(atom)).toEqual(0)
      expect(r.get(optimisticAtom)).toEqual(1)

      latch.openUnsafe()
      await Effect.runPromise(Effect.yieldNow)

      // commit phase: a refresh is triggered, the authoritative value is used
      expect(r.get(atom)).toEqual(2)
      expect(r.get(optimisticAtom)).toEqual(2)
    })

    it("Result", async () => {
      const runtime = Atom.runtime(Layer.empty)
      const latch = Latch.makeUnsafe()
      const r = AtomRegistry.make()
      let i = 0
      const atom = Atom.make(Effect.sync(() => {
        return i
      }))
      const optimisticAtom = atom.pipe(
        Atom.optimistic
      )
      const fn = optimisticAtom.pipe(
        Atom.optimisticFn({
          reducer: (_current, update: number) => AsyncResult.success(update),
          fn: runtime.fn(Effect.fnUntraced(function*() {
            yield* latch.await
          }))
        })
      )

      r.mount(optimisticAtom)
      r.mount(fn)

      expect(r.get(atom)).toEqual(AsyncResult.success(0))
      expect(r.get(optimisticAtom)).toEqual(AsyncResult.success(0))
      r.set(fn, 1)
      i = 2

      // optimistic phase: the optimistic value is set, but the true value is not
      expect(r.get(atom)).toEqual(AsyncResult.success(0))
      expect(r.get(optimisticAtom)).toEqual(AsyncResult.success(1, { waiting: true }))

      latch.openUnsafe()
      await Effect.runPromise(Effect.yieldNow)

      // commit phase: a refresh is triggered, the authoritative value is used
      expect(r.get(atom)).toEqual(AsyncResult.success(2))
      expect(r.get(optimisticAtom)).toEqual(AsyncResult.success(2))
    })

    it("failures", async () => {
      const latch = Latch.makeUnsafe()
      const r = AtomRegistry.make()
      const i = 0
      let rebuilds = 0
      const atom = Atom.make(() => {
        rebuilds++
        return i
      })
      const optimisticAtom = atom.pipe(
        Atom.optimistic
      )
      const fn = optimisticAtom.pipe(
        Atom.optimisticFn({
          reducer: (_, value) => value,
          fn: Atom.fn<number>()(Effect.fnUntraced(function*() {
            yield* latch.await
            return yield* Effect.fail("error")
          }))
        })
      )

      r.mount(fn)
      r.mount(optimisticAtom)

      expect(r.get(atom)).toEqual(0)
      expect(r.get(optimisticAtom)).toEqual(0)
      r.set(fn, 1)

      // optimistic phase: the optimistic value is set, but the true value is not
      expect(r.get(atom)).toEqual(0)
      expect(r.get(optimisticAtom)).toEqual(1)

      latch.openUnsafe()
      await Effect.runPromise(Effect.yieldNow)

      // commit phase: the optimistic value is reset to the true value
      expect(r.get(atom)).toEqual(0)
      expect(r.get(optimisticAtom)).toEqual(0)
      expect(rebuilds).toEqual(1)
    })

    it("sync fn", async () => {
      const r = AtomRegistry.make()
      let i = 0
      const atom = Atom.make(() => i)
      const optimisticAtom = atom.pipe(Atom.optimistic, Atom.keepAlive)
      const fn = optimisticAtom.pipe(
        Atom.optimisticFn({
          reducer: (_current, update) => update,
          fn: Atom.fn<number>()(() => {
            i = 2
            return Effect.void
          })
        })
      )

      expect(r.get(atom)).toEqual(0)
      expect(r.get(optimisticAtom)).toEqual(0)
      r.set(fn, 1)

      expect(r.get(atom)).toEqual(2)
      expect(r.get(optimisticAtom)).toEqual(2)
    })

    it("intermediate updates", async () => {
      const latch = Latch.makeUnsafe()
      const r = AtomRegistry.make()
      let i = 0
      const atom = Atom.make(Effect.sync(() => i))
      const optimisticAtom = atom.pipe(
        Atom.optimistic
      )
      const fn = optimisticAtom.pipe(
        Atom.optimisticFn({
          reducer: (_current, update: number) => AsyncResult.success(update),
          fn: (set) =>
            Atom.fn(Effect.fnUntraced(function*() {
              set(AsyncResult.success(123))
              yield* latch.await
            }))
        }),
        Atom.keepAlive
      )

      expect(r.get(atom)).toEqual(AsyncResult.success(0))
      assert.deepStrictEqual(r.get(optimisticAtom), AsyncResult.success(0))
      r.set(fn, 1)
      i = 2

      // optimistic phase: the intermediate value is set, but the true value is
      // not
      assert.deepStrictEqual(r.get(atom), AsyncResult.success(0))
      assert.deepStrictEqual(r.get(optimisticAtom), AsyncResult.success(123, { waiting: true }))

      latch.openUnsafe()
      await Effect.runPromise(Effect.yieldNow)

      // commit phase: a refresh is triggered, the authoritative value is used
      assert.deepStrictEqual(r.get(atom), AsyncResult.success(2))
      assert.deepStrictEqual(r.get(optimisticAtom), AsyncResult.success(2))
    })
  })

  describe("Reactivity", () => {
    it("rebuilds on mutation", async () => {
      const r = AtomRegistry.make()
      let rebuilds = 0
      const atom = Atom.make(() => rebuilds++).pipe(
        Atom.withReactivity(["counter"]),
        Atom.keepAlive
      )
      const fn = counterRuntime.fn(
        Effect.fn(function*() {
        }),
        { reactivityKeys: ["counter"] }
      )
      assert.strictEqual(r.get(atom), 0)
      r.set(fn, void 0)
      assert.strictEqual(r.get(atom), 1)
      r.set(fn, void 0)
      r.set(fn, void 0)
      assert.strictEqual(r.get(atom), 3)
    })

    it("rebuilds on mutation with a registry initial value", async () => {
      let rebuilds = 0
      let value = 0
      const atom = Atom.make(() => {
        rebuilds++
        return value
      }).pipe(
        Atom.withReactivity(["counter"]),
        Atom.keepAlive
      )
      const r = AtomRegistry.make({ initialValues: [Atom.initialValue(atom, 10)] })
      const fn = counterRuntime.fn(
        Effect.fn(function*() {
        }),
        { reactivityKeys: ["counter"] }
      )
      r.mount(atom)

      assert.strictEqual(r.get(atom), 10)
      assert.strictEqual(rebuilds, 1)

      value = 11
      r.set(fn, void 0)

      assert.strictEqual(r.get(atom), 11)
      assert.strictEqual(rebuilds, 2)
    })
  })

  it("Atom.Interrupt", async () => {
    const r = AtomRegistry.make()
    const atom = Atom.fn(() => Effect.never)
    r.mount(atom)
    expect(r.get(atom)).toEqual(AsyncResult.initial())
    expect(r.get(atom).waiting).toBeFalsy()
    r.set(atom, void 0)
    expect(r.get(atom)).toEqual(AsyncResult.initial(true))
    expect(r.get(atom).waiting).toBeTruthy()

    r.set(atom, Atom.Interrupt)
    await Effect.runPromise(Effect.yieldNow)
    const result = r.get(atom)
    expect(AsyncResult.isInterrupted(result)).toBeTruthy()
  })

  it("writable derived clears waiting after refresh", async () => {
    let count = 0
    const base = Atom.make(Effect.sync(() => ++count).pipe(Effect.delay(100))).pipe(
      Atom.withLabel("base")
    )
    const derived = Atom.writable(
      (get) => get(base),
      () => {},
      (refresh) => refresh(base)
    ).pipe(
      Atom.withLabel("derived")
    )
    const r = AtomRegistry.make()

    const unmount1 = r.mount(derived)
    await vitest.advanceTimersByTimeAsync(100)
    let result = r.get(derived)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(1)
    expect(result.waiting).toEqual(false)
    unmount1()

    r.refresh(derived)
    const unmount2 = r.mount(derived)
    await vitest.advanceTimersByTimeAsync(100)

    result = r.get(derived)
    expect(result.waiting).toEqual(false)
    assert(AsyncResult.isSuccess(result))
    expect(result.value).toEqual(2)

    unmount2()
  })

  it("get.result suspendOnWaiting", async () => {
    const r = AtomRegistry.make()

    const inner = Atom.make(Effect.succeed(1).pipe(Effect.delay(50)))
    const outer = Atom.make((get) => get.result(inner, { suspendOnWaiting: true }))

    r.mount(outer)

    let result = r.get(outer)
    assert(result.waiting)

    await vitest.advanceTimersByTimeAsync(50)

    result = r.get(outer)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value, 1)
  })

  it("fn get.result suspendOnWaiting", async () => {
    const r = AtomRegistry.make()
    let runs = 0

    const inner = Atom.fn((n: number) => {
      runs++
      return Effect.succeed(n * 2).pipe(Effect.delay(50))
    })

    const outer = Atom.fn(
      Effect.fn(function*(_: void, get: Atom.FnContext) {
        get.set(inner, 1)
        const a = yield* get.result(inner, { suspendOnWaiting: true })

        get.set(inner, 2)
        const b = yield* get.result(inner, { suspendOnWaiting: true })

        return { a, b }
      })
    )

    r.mount(outer)
    r.set(outer, void 0)

    await vitest.advanceTimersByTimeAsync(100)

    const result = r.get(outer)
    assert(AsyncResult.isSuccess(result))
    assert.strictEqual(result.value.a, 2)
    assert.strictEqual(result.value.b, 4)
    assert.strictEqual(runs, 2)
  })

  describe("kvs", () => {
    it("memoizes defaultValue while loading empty storage", async () => {
      let calls = 0
      const storage = new Map<string, string>()

      const DelayedKVS = Layer.succeed(
        KeyValueStore.KeyValueStore,
        KeyValueStore.makeStringOnly({
          get: (key) =>
            Effect.gen(function*() {
              yield* Effect.sleep(20)
              return storage.get(key)
            }),
          set: (key, value) =>
            Effect.sync(() => {
              storage.set(key, value)
            }),
          remove: (key) =>
            Effect.sync(() => {
              storage.delete(key)
            }),
          clear: Effect.sync(() => storage.clear()),
          size: Effect.sync(() => storage.size)
        })
      )

      const kvsRuntime = Atom.runtime(DelayedKVS)
      const atom = Atom.kvs({
        runtime: kvsRuntime,
        key: "default-value-key",
        schema: Schema.Number,
        defaultValue: () => {
          calls++
          return 0
        }
      })

      const r = AtomRegistry.make()
      r.mount(atom)

      expect(r.get(atom)).toEqual(0)
      expect(calls).toEqual(1)

      await vitest.advanceTimersByTimeAsync(50)

      expect(r.get(atom)).toEqual(0)
      expect(calls).toEqual(1)
    })

    it("preserves existing value after async load completes", async () => {
      vitest.useRealTimers()
      // Create an in-memory store with a pre-existing value
      const storage = new Map<string, string>()
      storage.set("test-key", JSON.stringify(42))

      // Create a delayed KeyValueStore to simulate async loading
      // Use KeyValueStore.make to get proper forSchema support
      const DelayedKVS = Layer.succeed(
        KeyValueStore.KeyValueStore,
        KeyValueStore.makeStringOnly({
          get: (key) =>
            Effect.gen(function*() {
              yield* Effect.sleep(20) // Short delay to create Initial state window
              return storage.get(key)
            }),
          set: (key, value) =>
            Effect.sync(() => {
              storage.set(key, value)
            }),
          remove: (key) =>
            Effect.sync(() => {
              storage.delete(key)
            }),
          clear: Effect.sync(() => storage.clear()),
          size: Effect.sync(() => storage.size)
        })
      )

      const kvsRuntime = Atom.runtime(DelayedKVS)
      const atom = Atom.kvs({
        runtime: kvsRuntime,
        key: "test-key",
        schema: Schema.Number,
        defaultValue: () => 0
      })

      const r = AtomRegistry.make()
      r.mount(atom)

      // First read during Initial state returns default
      const value = r.get(atom)
      expect(value).toEqual(0)

      // Wait for async load AND any set effects to complete
      await new Promise((resolve) => setTimeout(resolve, 50))

      // THE KEY ASSERTION: After load completes, storage should still have original value.
      // The bug was that the default (0) would be written during Initial state,
      // corrupting the storage before the async load could read it.
      expect(storage.get("test-key")).toEqual(JSON.stringify(42))
    })

    it("async mode", async () => {
      vitest.useRealTimers()
      // Create an in-memory store with a pre-existing value
      const storage = new Map<string, string>()
      storage.set("test-key", JSON.stringify(42))

      // Create a delayed KeyValueStore to simulate async loading
      // Use KeyValueStore.make to get proper forSchema support
      const DelayedKVS = Layer.succeed(
        KeyValueStore.KeyValueStore,
        KeyValueStore.makeStringOnly({
          get: (key) =>
            Effect.gen(function*() {
              yield* Effect.sleep(20) // Short delay to create Initial state window
              return storage.get(key)
            }),
          set: (key, value) =>
            Effect.sync(() => {
              storage.set(key, value)
            }),
          remove: (key) =>
            Effect.sync(() => {
              storage.delete(key)
            }),
          clear: Effect.sync(() => storage.clear()),
          size: Effect.sync(() => storage.size)
        })
      )

      const kvsRuntime = Atom.runtime(DelayedKVS)
      const atom = Atom.kvs({
        mode: "async",
        runtime: kvsRuntime,
        key: "test-key",
        schema: Schema.Number,
        defaultValue: () => 0
      })

      const r = AtomRegistry.make()
      r.mount(atom)

      expect(r.get(atom)).toEqual(AsyncResult.initial(true))

      await new Promise((resolve) => setTimeout(resolve, 50))

      const result = r.get(atom)
      assert(AsyncResult.isSuccess(result))
      expect(result.value).toEqual(42)
      expect(result.waiting).toEqual(false)
      expect(storage.get("test-key")).toEqual(JSON.stringify(42))

      r.set(atom, 24)

      const updated = r.get(atom)
      assert(AsyncResult.isSuccess(updated))
      expect(updated.value).toEqual(24)
    })
  })
})

interface BuildCounter {
  readonly get: Effect.Effect<number>
  readonly inc: Effect.Effect<void>
}
const BuildCounter = Context.Service<BuildCounter>("BuildCounter")
const BuildCounterLive = Layer.sync(BuildCounter, () => {
  let count = 0
  return BuildCounter.of({
    get: Effect.sync(() => count),
    inc: Effect.sync(() => {
      count++
    })
  })
})

interface Counter {
  readonly get: Effect.Effect<number>
  readonly inc: Effect.Effect<void>
}
const Counter = Context.Service<Counter>("Counter")
const CounterLive = Layer.effect(
  Counter,
  Effect.gen(function*() {
    const buildCounter = yield* BuildCounter
    yield* buildCounter.inc
    let count = 1
    return Counter.of({
      get: Effect.sync(() => count),
      inc: Effect.sync(() => {
        count++
      })
    })
  })
).pipe(
  Layer.provide(BuildCounterLive)
)

const CounterTest = Layer.effect(
  Counter,
  Effect.gen(function*() {
    const buildCounter = yield* BuildCounter
    yield* buildCounter.inc
    let count = 10
    return Counter.of({
      get: Effect.sync(() => count),
      inc: Effect.sync(() => {
        count++
      })
    })
  })
).pipe(
  Layer.provide(BuildCounterLive)
)

interface Multiplier {
  readonly times: (n: number) => Effect.Effect<number>
}
const Multiplier = Context.Service<Multiplier>("Multiplier")
const MultiplierLive = Layer.effect(
  Multiplier,
  Effect.gen(function*() {
    const counter = yield* Counter
    yield* AtomRegistry.AtomRegistry // test that we can access the registry
    return Multiplier.of({
      times: (n) => Effect.map(counter.get, (_) => _ * n)
    })
  })
).pipe(
  Layer.provideMerge(CounterLive)
)

const buildCounterRuntime = Atom.runtime(BuildCounterLive)
const counterRuntime = Atom.runtime(CounterLive)
const multiplierRuntime = Atom.runtime(MultiplierLive)
