import { test } from 'node:test';
import assert from 'node:assert/strict';
import { createStore } from 'jotai';
import { createListAtom } from './list';

// `createListAtom` produces a write atom whose reducer handles PUT (append a
// single item or an array of items), DELETE (identity-based removal of a single
// item or array of items) and REPLACE (identity match -> replacement). We drive
// the pure reducer through a real jotai store. The exported `TListAtom` type is
// a type alias only, nothing to test at runtime.

test('starts as an empty array', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  assert.deepEqual(store.get(listAtom), []);
});

test('PUT appends a single item', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: 'a' });
  store.set(listAtom, { type: 'PUT', item: 'b' });
  assert.deepEqual(store.get(listAtom), ['a', 'b']);
});

test('PUT appends an array of items in order', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: 'a' });
  store.set(listAtom, { type: 'PUT', item: ['b', 'c'] });
  assert.deepEqual(store.get(listAtom), ['a', 'b', 'c']);
});

test('PUT does not deduplicate', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: 'a' });
  store.set(listAtom, { type: 'PUT', item: 'a' });
  assert.deepEqual(store.get(listAtom), ['a', 'a']);
});

test('DELETE removes a single item by identity', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: ['a', 'b', 'c'] });
  store.set(listAtom, { type: 'DELETE', item: 'b' });
  assert.deepEqual(store.get(listAtom), ['a', 'c']);
});

test('DELETE removes an array of items', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: ['a', 'b', 'c', 'd'] });
  store.set(listAtom, { type: 'DELETE', item: ['a', 'c'] });
  assert.deepEqual(store.get(listAtom), ['b', 'd']);
});

test('DELETE of an absent item is a no-op', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: ['a', 'b'] });
  store.set(listAtom, { type: 'DELETE', item: 'z' });
  assert.deepEqual(store.get(listAtom), ['a', 'b']);
});

test('DELETE uses reference identity for object items', () => {
  const store = createStore();
  const listAtom = createListAtom<{ id: number }>();
  const a = { id: 1 };
  const b = { id: 2 };
  store.set(listAtom, { type: 'PUT', item: [a, b] });

  // A structurally-equal but distinct object is not removed.
  store.set(listAtom, { type: 'DELETE', item: { id: 1 } });
  assert.deepEqual(store.get(listAtom), [a, b]);

  // The same reference is removed.
  store.set(listAtom, { type: 'DELETE', item: a });
  assert.deepEqual(store.get(listAtom), [b]);
});

test('REPLACE swaps the matching item, preserving position', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: ['a', 'b', 'c'] });
  store.set(listAtom, { type: 'REPLACE', item: 'b', replacement: 'B' });
  assert.deepEqual(store.get(listAtom), ['a', 'B', 'c']);
});

test('REPLACE matches by identity and replaces every match', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: ['a', 'b', 'a'] });
  store.set(listAtom, { type: 'REPLACE', item: 'a', replacement: 'X' });
  assert.deepEqual(store.get(listAtom), ['X', 'b', 'X']);
});

test('REPLACE with no match leaves the list unchanged', () => {
  const store = createStore();
  const listAtom = createListAtom<string>();
  store.set(listAtom, { type: 'PUT', item: ['a', 'b'] });
  store.set(listAtom, { type: 'REPLACE', item: 'z', replacement: 'Z' });
  assert.deepEqual(store.get(listAtom), ['a', 'b']);
});
