import assert from 'node:assert/strict';
import { test } from 'node:test';
import { roomThreads, threadMatches } from '../src/lib/thread-index.ts';
import type { TimelineEvent, TimelineItem } from '../src/lib/types.ts';

function root(id: string, body: string, replies: number, latest: number, extra: Partial<TimelineEvent> = {}): TimelineItem {
  return {
    kind: 'event',
    event: {
      item_id: id,
      event_id: id,
      timestamp_ms: 1,
      sender: { user_id: '@a:x', display_name: 'Alice', avatar: null, name_ambiguous: false },
      content: { kind: 'Message', message: { kind: 'Text', body, media: null } },
      thread_root: null,
      thread_summary: replies ? { reply_count: replies, latest_event_id: null, latest_event_timestamp_ms: latest } : null,
      ...extra
    } as TimelineEvent
  };
}

test('threads sort by latest activity with pins first', () => {
  const items = [root('$a', 'old thread', 2, 100), root('$b', 'new thread', 1, 300), root('$c', 'no replies', 0, 0), root('$d', 'pinned', 4, 50)];
  const list = roomThreads('!r', items, ['$d']);
  assert.deepEqual(list.map((t) => t.root_event_id), ['$d', '$b', '$a']);
  assert.equal(list[0].pinned, true);
});

test('pinned threads outside the loaded timeline still get a row', () => {
  const list = roomThreads('!r', [], ['$gone']);
  assert.equal(list.length, 1);
  assert.equal(list[0].root_event_id, '$gone');
  assert.equal(list[0].preview, '');
});

test('replies inside a thread are not treated as roots', () => {
  const reply = root('$r', 'reply', 3, 10, { thread_root: '$a' });
  assert.equal(roomThreads('!r', [reply], []).length, 0);
});

test('search matches preview text and sender', () => {
  const [entry] = roomThreads('!r', [root('$a', 'Deploy plan for Friday', 2, 10)], []);
  assert.ok(threadMatches(entry, 'deploy'));
  assert.ok(threadMatches(entry, 'alice'));
  assert.equal(threadMatches(entry, 'lunch'), false);
});
