import { expect, test } from "bun:test";
import { createStoreContainer, defineStore } from "../src/index.ts";

const CounterStore = defineStore({
  name: "CounterStore",
  kind: "global" as const,
  createSharedState: () => ({ count: 0 }),
  createClientState: () => ({ viewport: 0 }),
  createServerState: () => ({ secret: "server-only" }),
  computed: { doubled: (state: Readonly<{ count: number }>) => state.count * 2 },
  actions: {
    increment: [
      {
        runtime: "client" as const,
        handler: ({ state }: any, amount = 1) => {
          state.count += amount;
        },
      },
      {
        runtime: "server" as const,
        handler: ({ state }: any, amount = 1) => {
          state.count += amount * 2;
        },
      },
    ],
  },
});

test("isolates request-scoped server stores and excludes server state", async () => {
  const first = createStoreContainer({ runtime: "server", request: {} });
  const second = createStoreContainer({ runtime: "server", request: {} });
  const a = await first.use(CounterStore);
  const b = await second.use(CounterStore);
  await a.increment(2);
  expect(a.count).toBe(4);
  expect(a.doubled).toBe(8);
  expect(b.count).toBe(0);
  expect(first.serialize()).toEqual({ CounterStore: { count: 4 } });
});

test("store state is readonly outside actions and supports snapshots/reset", async () => {
  const container = createStoreContainer({ runtime: "client" });
  const store = await container.use(CounterStore);
  expect(() => {
    (store.state as any).count = 5;
  }).toThrow("WRN-STORE-READONLY");
  await store.increment(3);
  expect(store.snapshot().count).toBe(3);
  store.reset();
  expect(store.count).toBe(0);
});

test("HMR preserves compatible state and resets incompatible fields", async () => {
  const container = createStoreContainer({ runtime: "client" });
  const store = await container.use(CounterStore);
  await store.increment(2);
  const result = await container.hotUpdate(
    defineStore({
      ...CounterStore,
      createSharedState: () => ({ count: 0, added: true }),
    } as any),
  );
  expect(result.preserved).toContain("count");
});

test("store lifecycle hooks may update state and page stores dispose cleanly", async () => {
  const calls: string[] = [];
  const LifecycleStore = defineStore({
    name: "LifecycleStore",
    kind: "page" as const,
    createSharedState: () => ({ ready: false }),
    lifecycle: {
      clientInit: async ({ state }: any) => {
        calls.push("clientInit");
        state.ready = true;
      },
      dispose: async ({ state }: any) => {
        calls.push("dispose");
        state.ready = false;
      },
    },
  });
  const container = createStoreContainer({ runtime: "client", routeId: "/one" });
  const store = await container.use(LifecycleStore);
  expect(store.ready).toBe(true);
  await container.disposePageStores();
  expect(calls).toEqual(["clientInit", "dispose"]);
});
