import { describe, it, expect, vi } from 'vitest'
import { render, screen, userEvent, waitFor } from '../../../test/utils'
import { UploadDropzone } from '../UploadDropzone'
import { createMockFile } from '../../../test/utils'

// react-dropzone file-type rejection requires a real browser File API that JSDOM
// doesn't fully implement, so we test the rejection-logic branch by overriding
// the module and calling onDrop with the correct rejectedFiles structure.
const mockOnDrop = vi.fn()
vi.mock('react-dropzone', async (importOriginal) => {
  const actual = await importOriginal<typeof import('react-dropzone')>()
  return {
    ...actual,
    useDropzone: (opts: Parameters<typeof actual.useDropzone>[0]) => {
      // Expose the onDrop handler so tests can invoke it directly
      mockOnDrop.mockImplementation((accepted: File[], rejected: Parameters<typeof opts.onDrop>[1]) => {
        // eslint-disable-next-line @typescript-eslint/no-explicit-any
        opts.onDrop?.(accepted, rejected, null as any)
      })
      return actual.useDropzone(opts)
    },
  }
})

describe('UploadDropzone', () => {
  it('renders default state correctly', () => {
    const onFilesSelect = vi.fn()
    render(<UploadDropzone onFilesSelect={onFilesSelect} />)

    expect(screen.getByText('Drag and drop a video file here, or click to select')).toBeInTheDocument()
    expect(screen.getByText('Supports MP4, MOV, AVI, MKV up to 1024MB')).toBeInTheDocument()
  })

  it('shows custom max size in text', () => {
    const onFilesSelect = vi.fn()
    const maxSize = 500 * 1024 * 1024

    render(<UploadDropzone onFilesSelect={onFilesSelect} maxSize={maxSize} />)

    expect(screen.getByText('Supports MP4, MOV, AVI, MKV up to 500MB')).toBeInTheDocument()
  })

  it('calls onFilesSelect when valid file is uploaded', async () => {
    const user = userEvent.setup()
    const onFilesSelect = vi.fn()
    const mockFile = createMockFile('test-video.mp4', 'video/mp4')

    render(<UploadDropzone onFilesSelect={onFilesSelect} />)

    const input = screen.getByRole('presentation').querySelector('input[type="file"]') as HTMLInputElement
    await user.upload(input, mockFile)

    await waitFor(() => {
      expect(onFilesSelect).toHaveBeenCalledWith([mockFile])
    })
  })

  it('shows error for invalid file type', async () => {
    const onFilesSelect = vi.fn()
    const invalidFile = createMockFile('document.pdf', 'application/pdf')

    render(<UploadDropzone onFilesSelect={onFilesSelect} />)

    // Simulate react-dropzone rejecting the file (JSDOM can't do this natively)
    mockOnDrop([], [{ file: invalidFile, errors: [{ code: 'file-invalid-type', message: 'Invalid type' }] }])

    await waitFor(() => {
      expect(screen.getByText('Invalid file type. Please upload a video file.')).toBeInTheDocument()
    })
    expect(onFilesSelect).not.toHaveBeenCalled()
  })

  it('shows error for file too large', async () => {
    const onFilesSelect = vi.fn()
    const maxSize = 100 * 1024 * 1024
    const largeFile = createMockFile('large-video.mp4', 'video/mp4', 200 * 1024 * 1024)

    render(<UploadDropzone onFilesSelect={onFilesSelect} maxSize={maxSize} />)

    mockOnDrop([], [{ file: largeFile, errors: [{ code: 'file-too-large', message: 'Too large' }] }])

    await waitFor(() => {
      expect(screen.getByText('File is too large. Maximum size is 100MB')).toBeInTheDocument()
    })
    expect(onFilesSelect).not.toHaveBeenCalled()
  })

  it('clears error state when new file is uploaded', async () => {
    const user = userEvent.setup()
    const onFilesSelect = vi.fn()
    const invalidFile = createMockFile('document.pdf', 'application/pdf')
    const validFile = createMockFile('video.mp4', 'video/mp4')

    render(<UploadDropzone onFilesSelect={onFilesSelect} />)
    const input = screen.getByRole('presentation').querySelector('input[type="file"]') as HTMLInputElement

    // Show error via rejected drop
    mockOnDrop([], [{ file: invalidFile, errors: [{ code: 'file-invalid-type', message: 'Invalid type' }] }])
    await waitFor(() => {
      expect(screen.getByText('Invalid file type. Please upload a video file.')).toBeInTheDocument()
    })

    // Upload valid file clears the error
    await user.upload(input, validFile)
    await waitFor(() => {
      expect(screen.queryByText('Invalid file type. Please upload a video file.')).not.toBeInTheDocument()
      expect(onFilesSelect).toHaveBeenCalledWith([validFile])
    })
  })

  it('applies disabled styling when disabled', () => {
    const onFilesSelect = vi.fn()
    render(<UploadDropzone onFilesSelect={onFilesSelect} disabled={true} />)

    const dropzone = screen.getByText('Drag and drop a video file here, or click to select').closest('[role="presentation"]')!
    expect(dropzone).toHaveClass('opacity-50', 'cursor-not-allowed')
  })

  it('handles custom accept types', () => {
    const onFilesSelect = vi.fn()
    render(<UploadDropzone onFilesSelect={onFilesSelect} accept={{ 'video/mp4': ['.mp4'] }} />)

    expect(screen.getByText('Drag and drop a video file here, or click to select')).toBeInTheDocument()
  })

  it('updates styling during drag events', () => {
    const onFilesSelect = vi.fn()
    render(<UploadDropzone onFilesSelect={onFilesSelect} />)

    const dropzone = screen.getByText('Drag and drop a video file here, or click to select').closest('[role="presentation"]')!
    expect(dropzone.className).toContain('border')
  })
})
