import { describe, expect, test } from 'bun:test'
import {
  getPastedTextRefNumLines,
  formatPastedTextRef,
  formatImageRef,
  parseReferences,
  expandPastedTextRefs,
} from '../history'

describe('getPastedTextRefNumLines', () => {
  test('returns 0 for single line (no newline)', () => {
    expect(getPastedTextRefNumLines('hello')).toBe(0)
  })

  test('counts LF newlines', () => {
    expect(getPastedTextRefNumLines('a\nb\nc')).toBe(2)
  })

  test('counts CRLF newlines', () => {
    expect(getPastedTextRefNumLines('a\r\nb')).toBe(1)
  })

  test('counts CR newlines', () => {
    expect(getPastedTextRefNumLines('a\rb')).toBe(1)
  })

  test('returns 0 for empty string', () => {
    expect(getPastedTextRefNumLines('')).toBe(0)
  })

  test('trailing newline counts as one', () => {
    expect(getPastedTextRefNumLines('a\n')).toBe(1)
  })
})

describe('formatPastedTextRef', () => {
  test('formats with lines count', () => {
    expect(formatPastedTextRef(1, 10)).toBe('[Pasted text #1 +10 lines]')
  })

  test('formats without lines when 0', () => {
    expect(formatPastedTextRef(3, 0)).toBe('[Pasted text #3]')
  })

  test('formats with large id', () => {
    expect(formatPastedTextRef(99, 5)).toBe('[Pasted text #99 +5 lines]')
  })
})

describe('formatImageRef', () => {
  test('formats image reference', () => {
    expect(formatImageRef(1)).toBe('[Image #1]')
  })

  test('formats with large id', () => {
    expect(formatImageRef(42)).toBe('[Image #42]')
  })
})

describe('parseReferences', () => {
  test('parses Pasted text ref', () => {
    const refs = parseReferences('[Pasted text #1 +5 lines]')
    expect(refs).toHaveLength(1)
    expect(refs[0]).toEqual({
      id: 1,
      match: '[Pasted text #1 +5 lines]',
      index: 0,
    })
  })

  test('parses Image ref', () => {
    const refs = parseReferences('[Image #2]')
    expect(refs).toHaveLength(1)
    expect(refs[0]!.id).toBe(2)
  })

  test('parses Truncated text ref', () => {
    const refs = parseReferences('[...Truncated text #3]')
    expect(refs).toHaveLength(1)
    expect(refs[0]!.id).toBe(3)
  })

  test('parses Pasted text without line count', () => {
    const refs = parseReferences('[Pasted text #4]')
    expect(refs).toHaveLength(1)
    expect(refs[0]!.id).toBe(4)
  })

  test('parses multiple refs', () => {
    const refs = parseReferences('hello [Pasted text #1] world [Image #2]')
    expect(refs).toHaveLength(2)
    expect(refs[0]!.id).toBe(1)
    expect(refs[1]!.id).toBe(2)
  })

  test('returns empty for no refs', () => {
    expect(parseReferences('plain text')).toEqual([])
  })

  test('filters out id 0', () => {
    const refs = parseReferences('[Pasted text #0]')
    expect(refs).toHaveLength(0)
  })

  test('captures correct index for embedded refs', () => {
    const input = 'prefix [Pasted text #1] suffix'
    const refs = parseReferences(input)
    expect(refs[0]!.index).toBe(7)
  })

  test('handles duplicate refs', () => {
    const refs = parseReferences('[Pasted text #1] and [Pasted text #1]')
    expect(refs).toHaveLength(2)
  })
})

describe('expandPastedTextRefs', () => {
  test('replaces single text ref', () => {
    const input = 'look at [Pasted text #1 +2 lines]'
    const pastedContents = {
      1: { id: 1, type: 'text' as const, content: 'line1\nline2\nline3' },
    }
    const result = expandPastedTextRefs(input, pastedContents)
    expect(result).toBe('look at line1\nline2\nline3')
  })

  test('replaces multiple text refs in reverse order', () => {
    const input = '[Pasted text #1] and [Pasted text #2]'
    const pastedContents = {
      1: { id: 1, type: 'text' as const, content: 'AAA' },
      2: { id: 2, type: 'text' as const, content: 'BBB' },
    }
    const result = expandPastedTextRefs(input, pastedContents)
    expect(result).toBe('AAA and BBB')
  })

  test('does not replace image refs', () => {
    const input = '[Image #1]'
    const pastedContents = {
      1: { id: 1, type: 'image' as const, content: 'data' },
    }
    const result = expandPastedTextRefs(input, pastedContents)
    expect(result).toBe('[Image #1]')
  })

  test('returns original when no refs', () => {
    const input = 'no refs here'
    const result = expandPastedTextRefs(input, {})
    expect(result).toBe('no refs here')
  })

  test('skips refs with no matching pasted content', () => {
    const input = '[Pasted text #99 +1 lines]'
    const result = expandPastedTextRefs(input, {})
    expect(result).toBe('[Pasted text #99 +1 lines]')
  })

  test('handles mixed content', () => {
    const input = 'see [Pasted text #1] and [Image #2]'
    const pastedContents = {
      1: { id: 1, type: 'text' as const, content: 'code here' },
      2: { id: 2, type: 'image' as const, content: 'img data' },
    }
    const result = expandPastedTextRefs(input, pastedContents)
    expect(result).toBe('see code here and [Image #2]')
  })
})
