import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStore } from 'jotai';
import { enableMapSet } from 'immer';
import type { Path } from 'react-router';

// `makeNavToActivePathAtom(userId)` is a factory: localStorage is read when the
// returned atom is first created/accessed (not at module load), but we still
// install the mock up front. The reducers `produce` over an immer-managed Map,
// so immer's Map/Set plugin must be enabled.
const NAV_TO_ACTIVE_PATH = 'navToActivePath';
const storeKeyFor = (userId: string): string => `${NAV_TO_ACTIVE_PATH}${userId}`;

const installStorage = (): Map<string, string> => {
  const map = new Map<string, string>();
  (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();
// eslint-disable-next-line import/first
import { makeNavToActivePathAtom } from './navToActivePath';

const USER = '@user:server';
const path = (pathname: string): Path => ({ pathname, search: '', hash: '' });

test('starts as an empty Map', () => {
  installStorage();
  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  const map = store.get(navAtom);
  assert.ok(map instanceof Map);
  assert.equal(map.size, 0);
});

test('PUT stores a path under its navId', () => {
  installStorage();
  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') });
  assert.deepEqual(store.get(navAtom).get('home'), path('/home'));
});

test('PUT overwrites the path for an existing navId', () => {
  installStorage();
  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/old') });
  store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/new') });
  const map = store.get(navAtom);
  assert.equal(map.size, 1);
  assert.deepEqual(map.get('home'), path('/new'));
});

test('DELETE removes a navId', () => {
  installStorage();
  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') });
  store.set(navAtom, { type: 'PUT', navId: 'dms', path: path('/dms') });
  store.set(navAtom, { type: 'DELETE', navId: 'home' });

  const map = store.get(navAtom);
  assert.equal(map.has('home'), false);
  assert.deepEqual(map.get('dms'), path('/dms'));
});

test('DELETE of an absent navId is a no-op', () => {
  installStorage();
  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') });
  store.set(navAtom, { type: 'DELETE', navId: 'ghost' });
  assert.deepEqual([...store.get(navAtom).keys()], ['home']);
});

test('persists to localStorage as an Object keyed per user', () => {
  const backing = installStorage();
  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  store.set(navAtom, { type: 'PUT', navId: 'home', path: path('/home') });

  const raw = backing.get(storeKeyFor(USER));
  assert.ok(raw);
  const obj = JSON.parse(raw!) as Record<string, Path>;
  assert.deepEqual(obj, { home: path('/home') });
});

test('hydrates the Map from a stored Object', () => {
  const backing = installStorage();
  backing.set(storeKeyFor(USER), JSON.stringify({ home: path('/home') }));

  const navAtom = makeNavToActivePathAtom(USER);
  const store = createStore();
  assert.deepEqual(store.get(navAtom).get('home'), path('/home'));
});

test('storage is scoped per userId', () => {
  const backing = installStorage();
  const userA = '@a:s';
  const userB = '@b:s';

  const atomA = makeNavToActivePathAtom(userA);
  const atomB = makeNavToActivePathAtom(userB);
  const store = createStore();

  store.set(atomA, { type: 'PUT', navId: 'home', path: path('/a-home') });
  store.set(atomB, { type: 'PUT', navId: 'home', path: path('/b-home') });

  assert.ok(backing.has(storeKeyFor(userA)));
  assert.ok(backing.has(storeKeyFor(userB)));
  assert.deepEqual(store.get(atomA).get('home'), path('/a-home'));
  assert.deepEqual(store.get(atomB).get('home'), path('/b-home'));
});
