import { render, screen, fireEvent } from '@testing-library/react'
import { DiffViewer, parseDiffFiles, diffLineClass } from '../diff/DiffViewer'

const SAMPLE_DIFF = `diff --git a/src/foo.ts b/src/foo.ts
index 1234567..89abcde 100644
--- a/src/foo.ts
+++ b/src/foo.ts
@@ -1,3 +1,3 @@
 const a = 1
-const b = 2
+const b = 3
diff --git a/src/bar.ts b/src/bar.ts
new file mode 100644
index 0000000..1111111
--- /dev/null
+++ b/src/bar.ts
@@ -0,0 +1,2 @@
+export const bar = 'baz'
+export default bar
`

describe('parseDiffFiles', () => {
  it('splits a unified diff into one block per file with the b/ path', () => {
    const files = parseDiffFiles(SAMPLE_DIFF)
    expect(files).toHaveLength(2)
    expect(files[0].filePath).toBe('src/foo.ts')
    expect(files[1].filePath).toBe('src/bar.ts')
    // the block carries its own lines (including the hunk header)
    expect(files[0].lines.some((l) => l.startsWith('@@'))).toBe(true)
  })

  it('returns an empty array for empty / whitespace input', () => {
    expect(parseDiffFiles('')).toEqual([])
    expect(parseDiffFiles('   \n  ')).toEqual([])
  })
})

describe('diffLineClass', () => {
  it('colourises added / removed / hunk / context lines', () => {
    expect(diffLineClass('+added')).toContain('emerald')
    expect(diffLineClass('-removed')).toContain('red')
    expect(diffLineClass('@@ -1 +1 @@')).toContain('cyan')
    expect(diffLineClass(' context')).toContain('muted-foreground')
  })
})

describe('DiffViewer', () => {
  it('renders per-file blocks and colourised +/-/@@ lines', () => {
    render(<DiffViewer diff={SAMPLE_DIFF} baseRef="main" />)
    // both file headers render
    expect(screen.getByText('src/foo.ts')).toBeInTheDocument()
    expect(screen.getByText('src/bar.ts')).toBeInTheDocument()
    // an added line is present and coloured; metadata lines are hidden
    expect(screen.getByText('+const b = 3')).toHaveClass('bg-emerald-500/10')
    expect(screen.getByText('-const b = 2')).toHaveClass('bg-red-500/10')
    expect(screen.queryByText(/^index 1234567/)).not.toBeInTheDocument()
    // base ref shown in header
    expect(screen.getByText('main')).toBeInTheDocument()
  })

  it('renders a summary line with staged/unstaged counts', () => {
    render(<DiffViewer diff={SAMPLE_DIFF} summary={{ totalFiles: 2, staged: 1, unstaged: 1 }} />)
    const summary = screen.getByTestId('diff-viewer-summary')
    expect(summary).toHaveTextContent('2 files changed')
    expect(summary).toHaveTextContent('1 staged')
    expect(summary).toHaveTextContent('1 unstaged')
  })

  it('shows the empty state (with custom label) when the diff is empty', () => {
    render(<DiffViewer diff="" emptyLabel="Nothing to review" />)
    expect(screen.getByTestId('diff-viewer-empty')).toBeInTheDocument()
    expect(screen.getByText('Nothing to review')).toBeInTheDocument()
  })

  it('shows a loading state', () => {
    render(<DiffViewer diff="" isLoading />)
    expect(screen.getByTestId('diff-viewer-loading')).toBeInTheDocument()
  })

  it('shows an error state and fires onRefresh from Try again', () => {
    const onRefresh = jest.fn()
    render(<DiffViewer diff="" error="boom" onRefresh={onRefresh} />)
    expect(screen.getByTestId('diff-viewer-error')).toBeInTheDocument()
    expect(screen.getByText('boom')).toBeInTheDocument()
    fireEvent.click(screen.getByRole('button', { name: /try again/i }))
    expect(onRefresh).toHaveBeenCalledTimes(1)
  })

  it('fires onRefresh from the header refresh button', () => {
    const onRefresh = jest.fn()
    render(<DiffViewer diff={SAMPLE_DIFF} onRefresh={onRefresh} />)
    fireEvent.click(screen.getByRole('button', { name: /refresh diff/i }))
    expect(onRefresh).toHaveBeenCalledTimes(1)
  })

  it('renders a collapsible working-tree status block', () => {
    render(<DiffViewer diff={SAMPLE_DIFF} status="?? untracked.ts" />)
    expect(screen.getByText('Working tree status')).toBeInTheDocument()
    expect(screen.getByText('?? untracked.ts')).toBeInTheDocument()
  })
})
