import { extractTrailingHashtags } from 'src/utils'

// `extractTrailingHashtags` pulls a trailing "wall of hashtags" off the end of
// a caption so it can render as pills below media. Its tokenizer now shares the
// canonical hashtag definition (`src/lib/text/fediverse-patterns`) with the
// inline autolinker, so the two can't disagree on what counts as a tag.

describe('extractTrailingHashtags', () => {
  test('pulls a trailing hashtag-only line off the body', () => {
    expect(extractTrailingHashtags('caption body\n#foo #bar')).toStrictEqual({
      body: 'caption body',
      hashtags: ['foo', 'bar'],
    })
  })

  test('keeps tags with digits intact (regression-adjacent)', () => {
    // `#summer2024` has a letter, so it is a valid tag and the line extracts.
    expect(extractTrailingHashtags('pic\n#summer2024 #beach')).toStrictEqual({
      body: 'pic',
      hashtags: ['summer2024', 'beach'],
    })
  })

  test('does NOT extract a line containing a pure-numeric token', () => {
    // `#100` is not a hashtag (no letter), so `#fun #100` is not an
    // all-hashtags line — it stays inline (where `#fun` still autolinks and
    // `#100` renders as plain text). Intentional change from the old loose
    // tokenizer, which allowed pure-numeric tokens.
    const input = 'hi\n#fun #100'
    expect(extractTrailingHashtags(input)).toStrictEqual({
      body: input,
      hashtags: [],
    })
  })

  test('does NOT extract a hyphenated pseudo-tag', () => {
    // `-` is not a hashtag char on either server, so `#foo-bar` is not a single
    // tag and the line is not all-hashtags. Stays inline.
    const input = 'hi\n#foo-bar'
    expect(extractTrailingHashtags(input)).toStrictEqual({
      body: input,
      hashtags: [],
    })
  })

  test('leaves a tags-only post inline (body-erasure guard)', () => {
    // Extracting would leave an empty body, so keep everything inline.
    const input = '#only #tags'
    expect(extractTrailingHashtags(input)).toStrictEqual({
      body: input,
      hashtags: [],
    })
  })

  test('stays linear-time on a hostile near-hashtag line (ReDoS guard)', () => {
    // Many valid-looking tags followed by a char that fails the all-hashtags
    // gate is the worst case for the `(?:…)+` wrapper. The canonical hashtag
    // body uses a letter-presence lookahead + a single greedy run so this is
    // linear; the earlier two-star `set*[letter]set*` body backtracked
    // exponentially (~3s at 6 tags). Don't reintroduce it.
    const tag = `#${'a'.repeat(20)} `
    const hostile = `body\n${tag.repeat(40)}x`
    const start = Date.now()
    const result = extractTrailingHashtags(hostile)
    expect(Date.now() - start).toBeLessThan(500)
    // Trailing `x` → the line isn't all hashtags → nothing is extracted.
    expect(result.hashtags).toEqual([])
  })
})
