import { fireEvent, render, screen, waitFor } from '@testing-library/react'
import { afterEach, beforeAll, describe, expect, it, jest } from '@jest/globals'

const mockUseUploaderController = jest.fn()

jest.mock('./uploader-controller', () => ({
  __esModule: true,
  default: (...args: unknown[]) => mockUseUploaderController(...args),
}))

// Defer module evaluation until after mocks are registered.
const UploaderView = require('./UploaderView').default

function makeFile(name: string, type: string) {
  return new File(['x'], name, { type })
}

function makeController(overrides: Record<string, unknown> = {}) {
  const setSel = jest.fn()
  const setBulkDesc = jest.fn()
  const setGlobalDate = jest.fn()
  const setLib = jest.fn()
  const setSub = jest.fn()
  const setNewFolderRaw = jest.fn()
  const refresh = jest.fn()
  return {
    mobile: false,
    me: { username: 'brad' },
    libs: ['alpha', 'beta'],
    lib: 'alpha',
    sub: 'videos',
    rootDirs: ['archive', 'videos'],
    rows: [],
    status: 'Ready',
    globalDate: '2026-04-10',
    uploading: false,
    sel: [
      { file: makeFile('photo.jpg', 'image/jpeg'), desc: '', date: '2026-04-10', finalName: '2026.04.10.upload.photo.jpg', progress: 0 },
      { file: makeFile('clip.mp4', 'video/mp4'), desc: '', date: '2026-04-10', finalName: '2026.04.10.upload.clip.mp4', progress: 0 },
      { file: makeFile('note.pdf', 'application/pdf'), desc: '', date: '2026-04-10', finalName: '2026.04.10.upload.note.pdf', progress: 0 },
    ],
    bulkDesc: 'holiday',
    folderInputRef: { current: document.createElement('input') },
    newFolderRaw: 'new-folder',
    setGlobalDate,
    setLib,
    setSub,
    setNewFolderRaw,
    setBulkDesc,
    setSel,
    handleChoose: jest.fn(),
    applyDescToAllVideos: jest.fn(),
    doUpload: jest.fn(),
    createSubfolder: jest.fn(),
    renameFolder: jest.fn(),
    deleteFolder: jest.fn(),
    renamePath: jest.fn(),
    deletePath: jest.fn(),
    refresh,
    sortedRows: [
      { name: 'archive', path: 'archive', is_dir: true, size: 0, mtime: 0 },
      { name: 'clip.mp4', path: 'clip.mp4', is_dir: false, size: 2048, mtime: 1713000000 },
    ],
    existingNames: new Set(['clip.mp4']),
    duplicateNamesInSelection: new Set(['2026.04.10.upload.clip.mp4']),
    hasNameIssues: false,
    destPath: '/alpha/videos',
    videosNeedingDesc: 0,
    ...overrides,
  }
}

beforeAll(() => {
  if (!('createObjectURL' in URL)) {
    Object.defineProperty(URL, 'createObjectURL', { value: jest.fn(() => 'blob:thumb'), configurable: true })
  } else {
    jest.spyOn(URL, 'createObjectURL').mockReturnValue('blob:thumb')
  }
  if (!('revokeObjectURL' in URL)) {
    Object.defineProperty(URL, 'revokeObjectURL', { value: jest.fn(), configurable: true })
  } else {
    jest.spyOn(URL, 'revokeObjectURL').mockImplementation(() => {})
  }
})

afterEach(() => {
  jest.clearAllMocks()
})

describe('UploaderView', () => {
  it('renders populated state and forwards interactions', async () => {
    const controller = makeController()
    mockUseUploaderController.mockReturnValue(controller)

    render(<UploaderView />)

    await waitFor(() => expect(screen.getByText('Signed in: brad')).toBeTruthy())
    expect(screen.getByText(/Destination:/)).toBeTruthy()
    expect(screen.getByText('Ready')).toBeTruthy()
    await waitFor(() => expect(document.querySelector('img')).not.toBeNull())
    expect(document.querySelector('video')).not.toBeNull()
    expect(screen.getByText('📄')).toBeTruthy()

    fireEvent.change(screen.getByLabelText('Default date'), { target: { value: '2026-04-11' } })
    fireEvent.change(screen.getByPlaceholderText('Short video description'), { target: { value: 'family trip' } })
    fireEvent.change(screen.getByLabelText('Select file(s)'), {
      target: { files: [makeFile('desktop.jpg', 'image/jpeg')] },
    })
    fireEvent.change(screen.getByLabelText('Select folder(s)'), {
      target: { files: [makeFile('folder.mp4', 'video/mp4')] },
    })

    const optionalImageInputs = screen.getAllByPlaceholderText('Optional for image')
    fireEvent.change(optionalImageInputs[0], { target: { value: 'photo desc' } })
    fireEvent.change(optionalImageInputs[1], { target: { value: 'note desc' } })
    fireEvent.change(screen.getByPlaceholderText('Required for video'), { target: { value: 'clip desc' } })
    fireEvent.change(screen.getAllByDisplayValue('2026-04-10')[1], { target: { value: '2026-04-12' } })
    fireEvent.change(screen.getByRole('combobox'), { target: { value: 'beta' } })

    expect(controller.setGlobalDate).toHaveBeenCalledWith('2026-04-11')
    expect(controller.setBulkDesc).toHaveBeenCalledWith('family trip')
    expect(controller.handleChoose).toHaveBeenCalled()
    expect(controller.setSel).toHaveBeenCalled()

    fireEvent.click(screen.getByRole('button', { name: 'Apply to all videos' }))
    fireEvent.change(screen.getByPlaceholderText('letters, numbers, underscores, dashes'), {
      target: { value: 'renamed-folder' },
    })
    fireEvent.click(screen.getByRole('button', { name: 'Create' }))
    fireEvent.click(screen.getByRole('button', { name: 'Rename' }))
    fireEvent.click(screen.getByLabelText('Go to library root'))
    fireEvent.click(screen.getByLabelText('Delete subfolder'))
    fireEvent.click(screen.getAllByRole('button', { name: 'Select' })[0])
    fireEvent.click(screen.getByRole('button', { name: 'Open' }))
    fireEvent.click(screen.getByLabelText('Delete clip.mp4'))
    fireEvent.click(screen.getByRole('button', { name: /Upload \(3\)/ }))

    expect(controller.applyDescToAllVideos).toHaveBeenCalled()
    expect(controller.setNewFolderRaw).toHaveBeenCalledWith('renamed-folder')
    expect(controller.createSubfolder).toHaveBeenCalledWith('new-folder')
    expect(controller.renameFolder).toHaveBeenCalledWith('videos')
    expect(controller.refresh).toHaveBeenCalled()
    expect(controller.deleteFolder).toHaveBeenCalledWith('videos')
    expect(controller.deletePath).toHaveBeenCalledWith('clip.mp4', false)
    expect(controller.doUpload).toHaveBeenCalled()
    expect(screen.getByText('photo.jpg')).toBeTruthy()
    expect(screen.getByText('clip.mp4')).toBeTruthy()
    expect(screen.getByText('note.pdf')).toBeTruthy()
  }, 15000)

  it('renders mobile file pickers and forwards capture/gallery selection', () => {
    const controller = makeController({
      mobile: true,
      sel: [],
      sortedRows: [],
      rootDirs: [],
      videosNeedingDesc: 0,
    })
    mockUseUploaderController.mockReturnValue(controller)

    render(<UploaderView />)

    fireEvent.change(screen.getByLabelText('Gallery/Photos'), {
      target: { files: [makeFile('photo.jpg', 'image/jpeg')] },
    })
    fireEvent.change(screen.getByLabelText('Camera (optional)'), {
      target: { files: [makeFile('clip.mp4', 'video/mp4')] },
    })

    expect(controller.handleChoose).toHaveBeenCalledTimes(2)
  })

  it('renders the empty-library state', () => {
    mockUseUploaderController.mockReturnValue(
      makeController({
        lib: '',
        sub: '',
        rootDirs: [],
        sel: [],
        sortedRows: [],
        videosNeedingDesc: 0,
        hasNameIssues: false,
        destPath: '/(choose a library)',
      })
    )

    render(<UploaderView />)

    expect(screen.getByText('Select at least one file.')).toBeTruthy()
    expect(screen.getByText('Select a library to create subfolders and view contents.')).toBeTruthy()
  })

  it('renders empty destination sections when the library has no children', () => {
    mockUseUploaderController.mockReturnValue(
      makeController({
        lib: 'alpha',
        sub: '',
        rootDirs: [],
        sel: [],
        sortedRows: [],
        destPath: '/alpha',
        videosNeedingDesc: 0,
      })
    )

    render(<UploaderView />)

    expect(screen.getByText(/No subfolders yet/)).toBeTruthy()
    expect(screen.getByText('Empty.')).toBeTruthy()
  })
})
