import { test } from 'node:test';
import assert from 'node:assert/strict';
import { MatrixClient, MatrixEvent } from 'matrix-js-sdk';
import { addRecentEmoji, getRecentEmojis, IRecentEmojiContent } from './recent-emoji';
import { AccountDataEvent } from '../../types/matrix/accountData';
import { emojis, loadEmojiData } from './emoji';

// A Map-backed MatrixClient stub supporting get/setAccountData.
const createMx = () => {
  const store = new Map<string, unknown>();
  const mx = {
    getAccountData: (type: string): MatrixEvent | undefined => {
      if (!store.has(type)) return undefined;
      const content = store.get(type);
      return { getContent: () => content } as unknown as MatrixEvent;
    },
    setAccountData: (type: string, content: unknown) => {
      store.set(type, content);
      return Promise.resolve({});
    },
  };
  return { mx: mx as unknown as MatrixClient, store };
};

const getStored = (store: Map<string, unknown>): IRecentEmojiContent['recent_emoji'] =>
  (store.get(AccountDataEvent.ElementRecentEmoji) as IRecentEmojiContent | undefined)?.recent_emoji;

// Emoji data is now loaded lazily; populate `emojis` before the round trips.
await loadEmojiData();

// Pick two real unicode emojis to drive add->get round trips.
const u1 = emojis[0].unicode;
const u2 = emojis[1].unicode;

test('getRecentEmojis returns [] when there is no account data', () => {
  const { mx } = createMx();
  assert.deepEqual(getRecentEmojis(mx), []);
});

test('getRecentEmojis returns [] when content is not an array', () => {
  const { mx, store } = createMx();
  store.set(AccountDataEvent.ElementRecentEmoji, { recent_emoji: 'nope' });
  assert.deepEqual(getRecentEmojis(mx), []);
});

test('addRecentEmoji creates an entry with count 1', () => {
  const { mx, store } = createMx();
  addRecentEmoji(mx, u1);
  assert.deepEqual(getStored(store), [[u1, 1]]);
});

test('adding the same emoji again increments its count and keeps it at front', () => {
  const { mx, store } = createMx();
  addRecentEmoji(mx, u1);
  addRecentEmoji(mx, u1);
  assert.deepEqual(getStored(store), [[u1, 2]]);
});

test('a newly added emoji is promoted to the front', () => {
  const { mx, store } = createMx();
  addRecentEmoji(mx, u1);
  addRecentEmoji(mx, u2);
  assert.deepEqual(getStored(store), [
    [u2, 1],
    [u1, 1],
  ]);
});

test('re-adding an existing emoji promotes it to front and increments', () => {
  const { mx, store } = createMx();
  addRecentEmoji(mx, u1);
  addRecentEmoji(mx, u2);
  addRecentEmoji(mx, u1);
  assert.deepEqual(getStored(store), [
    [u1, 2],
    [u2, 1],
  ]);
});

test('list is capped at 100 entries', () => {
  const { mx, store } = createMx();
  for (let i = 0; i < 120; i += 1) {
    addRecentEmoji(mx, `:emoji-${i}:`);
  }
  const stored = getStored(store)!;
  assert.equal(stored.length, 100);
  // most recently added is at the front
  assert.equal(stored[0][0], ':emoji-119:');
});

test('getRecentEmojis resolves stored unicodes to known emoji objects', () => {
  const { mx } = createMx();
  addRecentEmoji(mx, u1);
  addRecentEmoji(mx, u2);
  const recent = getRecentEmojis(mx);
  assert.equal(recent.length, 2);
  assert.equal(recent[0].unicode, u2);
  assert.equal(recent[1].unicode, u1);
});

test('getRecentEmojis skips unicodes with no matching emoji definition', () => {
  const { mx } = createMx();
  addRecentEmoji(mx, ':not-a-real-emoji:');
  addRecentEmoji(mx, u1);
  const recent = getRecentEmojis(mx);
  assert.equal(recent.length, 1);
  assert.equal(recent[0].unicode, u1);
});

test('getRecentEmojis respects the limit argument (slice before resolve)', () => {
  const { mx } = createMx();
  addRecentEmoji(mx, u1);
  addRecentEmoji(mx, u2);
  const recent = getRecentEmojis(mx, 1);
  assert.equal(recent.length, 1);
  assert.equal(recent[0].unicode, u2);
});
