import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStore } from 'jotai';
import { enableMapSet } from 'immer';

// `spaceRoomsAtom` is backed by `atomWithLocalStorage`, which reads
// `localStorage` AT MODULE LOAD to seed the atom (absent in node). We install an
// in-memory mock first. The reducers also `produce` over an immer-managed Set,
// so immer's Map/Set plugin must be enabled (the app does this at startup).
const SPACE_ROOMS = 'spaceRooms';

const installStorage = (seed?: string[]): Map<string, string> => {
  const map = new Map<string, string>();
  if (seed) map.set(SPACE_ROOMS, JSON.stringify(seed));
  (globalThis as { localStorage?: unknown }).localStorage = {
    getItem: (k: string) => (map.has(k) ? map.get(k)! : null),
    setItem: (k: string, v: string) => {
      map.set(k, v);
    },
    removeItem: (k: string) => {
      map.delete(k);
    },
  };
  return map;
};

enableMapSet();
installStorage();
const { spaceRoomsAtom } = await import('./spaceRooms');

// `atomWithLocalStorage` seeds the atom from storage exactly once, when the
// module is evaluated. To exercise hydration we install seeded storage and then
// import a *fresh* copy of the module (cache-busted) so the seed is read.
let freshCounter = 0;
const importWithStorage = async (
  seed?: string[],
): Promise<typeof import('./spaceRooms').spaceRoomsAtom> => {
  installStorage(seed);
  freshCounter += 1;
  const mod = await import(`./spaceRooms?fresh=${freshCounter}`);
  return mod.spaceRoomsAtom;
};

const sorted = (s: Set<string>): string[] => [...s].sort();

test('starts empty when nothing is stored', () => {
  installStorage();
  const store = createStore();
  assert.equal(store.get(spaceRoomsAtom).size, 0);
});

test('PUT adds new room ids to the Set', () => {
  installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
  assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!a:s', '!b:s']);
});

test('PUT dedupes against existing entries', () => {
  installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
  assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!a:s', '!b:s']);
});

test('PUT with only already-present ids does not write (Set identity unchanged)', () => {
  installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
  const before = store.get(spaceRoomsAtom);

  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
  const after = store.get(spaceRoomsAtom);

  // No new entries -> the reducer skips set(), so the Set reference is the same.
  assert.equal(before, after);
});

test('DELETE removes present members', () => {
  installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
  store.set(spaceRoomsAtom, { type: 'DELETE', roomIds: ['!a:s'] });
  assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!b:s']);
});

test('DELETE of an absent member does not write (Set identity unchanged)', () => {
  installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s'] });
  const before = store.get(spaceRoomsAtom);

  store.set(spaceRoomsAtom, { type: 'DELETE', roomIds: ['!nope:s'] });
  const after = store.get(spaceRoomsAtom);

  assert.equal(before, after);
  assert.deepEqual(sorted(after), ['!a:s']);
});

test('DELETE acts when at least one of the ids is present', () => {
  installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });
  store.set(spaceRoomsAtom, { type: 'DELETE', roomIds: ['!a:s', '!nope:s'] });
  assert.deepEqual(sorted(store.get(spaceRoomsAtom)), ['!b:s']);
});

test('hydrates the Set from a stored array', async () => {
  const freshAtom = await importWithStorage(['!x:s', '!y:s']);
  const store = createStore();
  assert.deepEqual(sorted(store.get(freshAtom)), ['!x:s', '!y:s']);
});

test('persists the Set to localStorage as an array', () => {
  const backing = installStorage();
  const store = createStore();
  store.set(spaceRoomsAtom, { type: 'PUT', roomIds: ['!a:s', '!b:s'] });

  const raw = backing.get(SPACE_ROOMS);
  assert.ok(raw);
  const arr = JSON.parse(raw!) as string[];
  assert.deepEqual([...arr].sort(), ['!a:s', '!b:s']);
});
