// Unit tests for the incoming-note popup queue.
//
// The queue is the data structure behind the centered modal that
// appears on the recipient's screen the moment a `note_received` socket
// event arrives. It must:
//   - Be FIFO (notes pop in arrival order)
//   - Drop duplicates by note id (re-emits or multi-tab races)
//   - Suppress events for the user's own sends (sender id === me)
//   - Allow filtering out a specific id from the middle of the queue
//
// `applyEnqueue` and `applyDismiss` are exported as pure helpers from
// the context module so we can test the rules directly without React.
import { test } from "node:test";
import assert from "node:assert/strict";
import {
  applyEnqueue,
  applyDismiss,
} from "../lib/incoming-note-queue.ts";

function note(id, senderUserId, extra = {}) {
  return {
    noteId: id,
    senderUserId,
    title: `Note ${id}`,
    content: "",
    color: "default",
    sender: null,
    ...extra,
  };
}

test("applyEnqueue appends new notes in arrival order (FIFO)", () => {
  let q = [];
  q = applyEnqueue(q, note(1, 99), 7);
  q = applyEnqueue(q, note(2, 99), 7);
  q = applyEnqueue(q, note(3, 99), 7);
  assert.deepEqual(
    q.map((p) => p.noteId),
    [1, 2, 3],
  );
});

test("applyEnqueue dedupes by noteId — second copy is ignored", () => {
  let q = [];
  q = applyEnqueue(q, note(42, 99), 7);
  const before = q;
  q = applyEnqueue(q, note(42, 99, { title: "should be ignored" }), 7);
  assert.equal(q, before, "queue reference is unchanged on dedupe");
  assert.equal(q.length, 1);
  assert.equal(q[0].title, "Note 42");
});

test("applyEnqueue suppresses notes sent by the current user", () => {
  let q = [];
  // Same user is sender + recipient (multi-tab edge case).
  q = applyEnqueue(q, note(1, 7), 7);
  assert.equal(q.length, 0);
  // Other senders still get through.
  q = applyEnqueue(q, note(2, 8), 7);
  assert.equal(q.length, 1);
  assert.equal(q[0].noteId, 2);
});

test("applyEnqueue does NOT suppress when current user is unknown (null)", () => {
  let q = [];
  q = applyEnqueue(q, note(1, 7), null);
  assert.equal(q.length, 1, "unknown current user should not filter anyone");
});

test("applyEnqueue rejects malformed payloads", () => {
  let q = [];
  q = applyEnqueue(q, null, 7);
  q = applyEnqueue(q, { senderUserId: 1 }, 7);
  q = applyEnqueue(q, { noteId: "not-a-number", senderUserId: 1 }, 7);
  assert.deepEqual(q, []);
});

test("applyDismiss pops the head when it matches", () => {
  let q = [note(1, 99), note(2, 99), note(3, 99)];
  q = applyDismiss(q, 1);
  assert.deepEqual(
    q.map((p) => p.noteId),
    [2, 3],
  );
});

test("applyDismiss filters from the middle when head doesn't match", () => {
  let q = [note(1, 99), note(2, 99), note(3, 99)];
  q = applyDismiss(q, 2);
  assert.deepEqual(
    q.map((p) => p.noteId),
    [1, 3],
  );
});

test("applyDismiss is a no-op on an empty queue or missing id", () => {
  assert.deepEqual(applyDismiss([], 1), []);
  const q = [note(1, 99)];
  assert.equal(applyDismiss(q, 999), q);
});

test("FIFO + dedupe + dismiss compose correctly across many ops", () => {
  let q = [];
  q = applyEnqueue(q, note(1, 99), 7);
  q = applyEnqueue(q, note(2, 99), 7);
  q = applyEnqueue(q, note(2, 99), 7); // dupe — ignored
  q = applyEnqueue(q, note(3, 7), 7); // self-send — suppressed
  q = applyEnqueue(q, note(4, 99), 7);
  assert.deepEqual(
    q.map((p) => p.noteId),
    [1, 2, 4],
  );
  q = applyDismiss(q, 1);
  q = applyDismiss(q, 4);
  assert.deepEqual(
    q.map((p) => p.noteId),
    [2],
  );
});
