import '@testing-library/jest-dom'

import { render, screen } from '@testing-library/react'
import { vi } from 'vitest'

import { FileItem, TFileItemList, TFileTransfer } from './types'
import { PktFileUpload } from './FileUpload'

// Polyfill for ResizeObserver
if (!global.ResizeObserver) {
  class ResizeObserverPolyfill {
    observe() {}
    unobserve() {}
    disconnect() {}
  }

  global.ResizeObserver = ResizeObserverPolyfill as any
}

const NOOP = () => {}

const createMockFile = (name: string, type = 'text/plain'): File => new File(['content'], name, { type })

const createFileItem = (name: string, fileId?: string) => new FileItem(createMockFile(name), fileId)

const expectVisibleFilename = (filename: string) => {
  // Truncate may render the filename as a single span (data-pkt-truncate-part="first")
  // or as a head + tail pair when the name is long enough to split. Assemble the parts
  // from each title element and assert the filename is found in at least one.
  const titleElements = document.querySelectorAll('.pkt-fileupload__queue-display__item__title')
  const reconstructed = Array.from(titleElements).map((title) => {
    const head = title.querySelector('[data-pkt-truncate-part="first"]')?.textContent ?? ''
    const tail = title.querySelector('[data-pkt-truncate-part="tail"]')?.textContent ?? ''
    return head + tail
  })
  expect(reconstructed).toContain(filename)
}

describe('QueueDisplay', () => {
  describe('Progress states', () => {
    it.each([
      {
        state: 'queued',
        progress: 'queued' as TFileTransfer['progress'],
        filename: 'queued-file.pdf',
        description: 'when no transfer exists',
      },
      {
        state: 'done',
        progress: 'done' as TFileTransfer['progress'],
        filename: 'completed-file.pdf',
        description: 'after successful upload',
      },
      {
        state: 'error',
        progress: 'error' as TFileTransfer['progress'],
        filename: 'failed-file.pdf',
        description: 'after failed upload',
      },
      {
        state: 'canceled',
        progress: 'canceled' as TFileTransfer['progress'],
        filename: 'canceled-file.pdf',
        description: 'when canceled',
      },
      // { state: 'in-progress', progress: 10 as FileTransfer['progress'], filename: 'in-progress-file.pdf', description: 'when canceled' },
    ])('should display file with $state state $description', ({ progress, filename, state }) => {
      const initialValue: TFileItemList = [createFileItem(filename, 'file-1')]
      const transfers = progress !== undefined ? [{ fileId: 'file-1', progress }] : []

      const { container } = render(
        <PktFileUpload
          id={'pktFileUploadId'}
          value={initialValue}
          name={'pktFileUpload'}
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={transfers}
          onFilesChanged={NOOP}
        />,
      )

      // File should be displayed
      expectVisibleFilename(filename)

      // Should have proper class when not in progress
      const queueItem = container.querySelector('.pkt-fileupload__queue-display__item')
      expect(queueItem).toHaveClass('pkt-fileupload__queue-display__item--' + state)
      expect(queueItem).not.toHaveClass('pkt-fileupload__queue-display__item--in-progress')

      // Should not show progress bar or cancel button
      expect(container.querySelector('.pkt-fileupload__queue-display__item__progress')).not.toBeInTheDocument()
      expect(screen.queryByText('Avbryt')).not.toBeInTheDocument()
    })

    it('should display file with in-progress state and progress bar', () => {
      const initialValue: TFileItemList = [createFileItem('uploading-file.pdf', 'file-1')]

      const { container } = render(
        <PktFileUpload
          id={'pktFileUploadId'}
          value={initialValue}
          name={'pktFileUpload'}
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={[{ fileId: 'file-1', progress: 0.45 }]}
          onFilesChanged={NOOP}
        />,
      )

      // File should be displayed
      expectVisibleFilename('uploading-file.pdf')

      // Should have the 'in-progress' class
      const queueItem = container.querySelector('.pkt-fileupload__queue-display__item')
      expect(queueItem).toHaveClass('pkt-fileupload__queue-display__item--in-progress')
      expect(queueItem).not.toHaveClass('pkt-fileupload__queue-display__item--done')

      // Should show progress bar
      expect(container.querySelector('.pkt-fileupload__queue-display__item__progress')).toBeInTheDocument()

      // Should show percentage
      expect(screen.getByText('45%')).toBeInTheDocument()

      // Should show cancel button
      expect(screen.getByText('Avbryt')).toBeInTheDocument()
    })

    it('should display multiple files with different progress states', () => {
      const initialValue: TFileItemList = [
        createFileItem('queued-file.pdf', 'file-1'),
        createFileItem('uploading-file.pdf', 'file-2'),
        createFileItem('completed-file.pdf', 'file-3'),
        createFileItem('failed-file.pdf', 'file-4'),
      ]

      const { container } = render(
        <PktFileUpload
          id={'pktFileUploadId'}
          value={initialValue}
          name={'pktFileUpload'}
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={[
            { fileId: 'file-2', progress: 0.75 },
            { fileId: 'file-3', progress: 'done' },
            { fileId: 'file-4', progress: 'error' },
          ]}
          onFilesChanged={NOOP}
        />,
      )

      // All files should be displayed
      expectVisibleFilename('queued-file.pdf')
      expectVisibleFilename('uploading-file.pdf')
      expectVisibleFilename('completed-file.pdf')
      expectVisibleFilename('failed-file.pdf')

      // Should have 4 queue items
      const queueItems = container.querySelectorAll('.pkt-fileupload__queue-display__item')
      expect(queueItems).toHaveLength(4)

      // Only the uploading file should have in-progress class
      const inProgressItems = container.querySelectorAll('.pkt-fileupload__queue-display__item--in-progress')
      expect(inProgressItems).toHaveLength(1)

      // Only the uploading file should show progress bar and percentage
      expect(screen.getByText('75%')).toBeInTheDocument()
      expect(container.querySelectorAll('.pkt-fileupload__queue-display__item__progress')).toHaveLength(1)
    })

    it('should update progress percentage when transfer progresses', () => {
      const initialValue: TFileItemList = [createFileItem('uploading-file.pdf', 'file-1')]

      const { container, rerender } = render(
        <PktFileUpload
          id={'pktFileUploadId'}
          value={initialValue}
          name={'pktFileUpload'}
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={[{ fileId: 'file-1', progress: 0.25 }]}
          onFilesChanged={NOOP}
        />,
      )

      // Initially at 25%
      expect(screen.getByText('25%')).toBeInTheDocument()

      // Update to 50%
      rerender(
        <PktFileUpload
          id={'pktFileUploadId'}
          value={initialValue}
          name={'pktFileUpload'}
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={[{ fileId: 'file-1', progress: 0.5 }]}
          onFilesChanged={NOOP}
        />,
      )

      expect(screen.queryByText('25%')).not.toBeInTheDocument()
      expect(screen.getByText('50%')).toBeInTheDocument()

      // Update to done
      rerender(
        <PktFileUpload
          id={'pktFileUploadId'}
          value={initialValue}
          name={'pktFileUpload'}
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={[{ fileId: 'file-1', progress: 'done' }]}
          onFilesChanged={NOOP}
        />,
      )

      // Progress percentage should no longer be visible
      expect(screen.queryByText('50%')).not.toBeInTheDocument()
      expect(container.querySelector('.pkt-fileupload__queue-display__item__progress')).not.toBeInTheDocument()
    })
  })

  describe('Sort order', () => {
    it('renders queue items in the order: in-progress → error → done/queued', () => {
      const initialValue: TFileItemList = [
        createFileItem('done.pdf', 'done-1'),
        createFileItem('failed.pdf', 'error-1'),
        createFileItem('uploading.pdf', 'progress-1'),
        createFileItem('queued.pdf', 'queued-1'),
      ]
      const transfers: TFileTransfer[] = [
        { fileId: 'done-1', progress: 'done' },
        { fileId: 'error-1', progress: 'error', errorMessage: 'failed' },
        { fileId: 'progress-1', progress: 0.5, showProgress: true },
        { fileId: 'queued-1', progress: 'queued' },
      ]

      const { container } = render(
        <PktFileUpload
          id="sort-order-test"
          value={initialValue}
          name="pktFileUpload"
          uploadStrategy="custom"
          onFileUploadRequested={vi.fn()}
          transfers={transfers}
          onFilesChanged={NOOP}
        />,
      )

      // Reconstruct each filename from head + tail spans (middle-truncation may split it).
      const queueItems = Array.from(
        container.querySelectorAll<HTMLElement>('.pkt-fileupload__queue-display__item'),
      )
      const visibleNames = queueItems.map((item) => {
        const head = item.querySelector<HTMLElement>('[data-pkt-truncate-part="first"]')?.textContent ?? ''
        const tail = item.querySelector<HTMLElement>('[data-pkt-truncate-part="tail"]')?.textContent ?? ''
        return (head + tail).trim()
      })

      expect(visibleNames).toEqual(['uploading.pdf', 'failed.pdf', 'done.pdf', 'queued.pdf'])
    })
  })
})
