import assert from 'node:assert/strict';
import { test } from 'node:test';
import { enabledQuickReactions } from '../src/lib/pack-preferences-model.ts';
import {
  isSeparator,
  normaliseQuick,
  parseQuickReactions,
  quickColumns,
  quickGroups,
  sameQuick
} from '../src/lib/quick-reactions-model.ts';
import type { QuickEntry } from '../src/lib/types.ts';

const r = (key: string): QuickEntry => ({ key, shortcode: null });
const sep = (): QuickEntry => ({ separator: true });
const shape = (list: QuickEntry[]) => list.map((e) => (isSeparator(e) ? '|' : e.key)).join(' ');

test('normalising drops leading, trailing and doubled separators', () => {
  assert.equal(shape(normaliseQuick([sep(), r('a'), sep(), sep(), r('b'), sep()])), 'a | b');
  assert.equal(shape(normaliseQuick([sep(), sep()])), '');
  assert.equal(shape(normaliseQuick([])), '');
});

test('normalising dedupes keys and keeps the break between survivors', () => {
  assert.equal(shape(normaliseQuick([r('a'), sep(), r('a'), sep(), r('b')])), 'a | b');
  assert.equal(shape(normaliseQuick([r('a'), r('b'), sep(), r('a'), sep(), r('c')])), 'a b | c');
});

test('normalising keeps separator identity and leaves the input alone', () => {
  const middle = sep();
  const input = [r('a'), middle, r('b'), sep()];
  const out = normaliseQuick(input);
  assert.equal(out[1], middle);
  assert.equal(input.length, 4);
});

test('parsing keeps separators and tolerates what older builds wrote', () => {
  const parsed = parseQuickReactions({
    reactions: [{ key: 'a', shortcode: 'x' }, { separator: true }, { key: 'b' }, 7, null, { key: '' }, { key: 'a' }, { separator: true }]
  });
  assert.deepEqual(parsed, [{ key: 'a', shortcode: 'x' }, { separator: true }, { key: 'b', shortcode: null }]);
});

test('parsing rejects data without a reactions list', () => {
  assert.equal(parseQuickReactions(null), null);
  assert.equal(parseQuickReactions({ reactions: 'a' }), null);
  assert.equal(parseQuickReactions('a'), null);
  assert.deepEqual(parseQuickReactions({ reactions: [] }), []);
});

test('only a literal true makes a separator', () => {
  assert.deepEqual(parseQuickReactions({ reactions: [{ separator: 'yes' }, { separator: false, key: 'a' }] }), [{ key: 'a', shortcode: null }]);
});

test('groups split at separators and skip empty runs', () => {
  const groups = quickGroups([r('a'), r('b'), sep(), sep(), r('c'), sep()]);
  assert.deepEqual(groups.map((g) => g.map((e) => e.key)), [['a', 'b'], ['c']]);
  assert.deepEqual(quickGroups([]), []);
  assert.deepEqual(quickGroups([sep()]), []);
});

test('the toolbar is as wide as its widest group up to the row limit', () => {
  const groups = quickGroups([r('a'), r('b'), r('c'), sep(), r('d')]);
  assert.equal(quickColumns(groups, 8), 3);
  assert.equal(quickColumns(groups, 2), 2);
  assert.equal(quickColumns([], 8), 0);
});

test('comparison looks at order, keys and separator positions', () => {
  assert.ok(sameQuick([r('a'), sep(), r('b')], [r('a'), sep(), r('b')]));
  assert.ok(!sameQuick([r('a'), sep(), r('b')], [r('a'), r('b')]));
  assert.ok(!sameQuick([r('a'), r('b')], [r('b'), r('a')]));
  assert.ok(!sameQuick([sep()], [r('a')]));
});

test('a hidden pack image leaves no empty group or doubled break', () => {
  const pack = {
    id: 'p',
    display_name: null,
    avatar: null,
    attribution: 'source',
    source: 'User' as const,
    images: [{ shortcode: 'one', media_id: 'mxc://x/one', reaction_key: 'mxc://x/one', body: null, mimetype: null, width: null, height: null, usage: ['Emoticon' as const] }],
    editable: true,
    enabled_everywhere: false
  };
  const list = [r('a'), sep(), r('mxc://x/one'), sep(), r('b')];
  const visible = normaliseQuick(enabledQuickReactions(list, [pack], { p: ['one'] }));
  assert.equal(shape(visible), 'a | b');
  assert.equal(shape(normaliseQuick(enabledQuickReactions([r('mxc://x/one'), sep(), r('a')], [pack], { p: ['one'] }))), 'a');
});
