import { afterAll, beforeAll, describe, expect, it, jest } from '@jest/globals'

import uploaderUtils from './uploader-utils'

const {
  clampOneLevel,
  composeName,
  extOf,
  createNoResumeFingerprint,
  createNoResumeStorage,
  fmt,
  isDetailedError,
  isImageFile,
  isLikelyMobileUA,
  isVideoFile,
  normalizeRows,
  sanitizeDesc,
  sanitizeFolderName,
  stemOf,
} = uploaderUtils

describe('Uploader helpers', () => {
  let logSpy: ReturnType<typeof jest.spyOn>

  beforeAll(() => {
    logSpy = jest.spyOn(console, 'log').mockImplementation(() => {})
  })

  afterAll(() => {
    logSpy.mockRestore()
  })

  it('sanitizes description and folder names', () => {
    expect(sanitizeDesc('  Hello world!  ')).toBe('Hello_world_')
    expect(sanitizeDesc('')).toBe('upload')
    expect(sanitizeFolderName('  /my folder/with/depth  ')).toBe('my_folder')
    expect(sanitizeFolderName('bad#$name')).toBe('bad_name')
  })

  it('computes extension, stem, and composed final name', () => {
    expect(extOf('clip.MP4')).toBe('mp4')
    expect(extOf('noext')).toBe('')
    expect(stemOf('My.Video.File.mp4')).toBe('My_Video_File')
    expect(composeName('2026-04-09', 'Trip Clip', 'My.Video.File.mp4')).toBe(
      '2026.04.09.Trip_Clip.My_Video_File.mp4',
    )
  })

  it('clamps one-level paths and normalizes row payload shapes', () => {
    expect(clampOneLevel('/photos/2026/trip/')).toBe('photos')
    expect(clampOneLevel('')).toBe('')

    const rows = normalizeRows([
      { Name: 'a', Path: 'a', IsDir: true, Size: 12, Mtime: 55 },
      { name: 'b', path: 'b', is_dir: false, size: 3, mtime: 99 },
      null,
    ] as any)

    expect(rows[0]).toEqual({ name: 'a', path: 'a', is_dir: true, size: 12, mtime: 55 })
    expect(rows[1]).toEqual({ name: 'b', path: 'b', is_dir: false, size: 3, mtime: 99 })
    expect(rows[2]).toEqual({ name: '', path: '', is_dir: false, size: 0, mtime: 0 })
  })

  it('detects video/image files by type and extension', () => {
    const mkFile = (name: string, type: string) => new File(['x'], name, { type })
    expect(isVideoFile(mkFile('x.mp4', 'video/mp4'))).toBe(true)
    expect(isVideoFile(mkFile('x.mkv', ''))).toBe(true)
    expect(isVideoFile(mkFile('x.txt', 'text/plain'))).toBe(false)

    expect(isImageFile(mkFile('x.jpg', 'image/jpeg'))).toBe(true)
    expect(isImageFile(mkFile('x.heic', ''))).toBe(true)
    expect(isImageFile(mkFile('x.mp4', 'video/mp4'))).toBe(false)
  })

  it('identifies detailed tus errors and mobile UA heuristics', () => {
    expect(isDetailedError({ originalRequest: {} })).toBe(true)
    expect(isDetailedError({ originalResponse: {} })).toBe(true)
    expect(isDetailedError({})).toBe(false)

    expect(typeof isLikelyMobileUA()).toBe('boolean')
  })

  it('covers the mobile UA and size formatting branches', () => {
    const originalMatchMedia = window.matchMedia
    Object.defineProperty(window, 'matchMedia', {
      configurable: true,
      value: () => ({ matches: true }),
    })
    expect(isLikelyMobileUA()).toBe(true)

    Object.defineProperty(window, 'matchMedia', {
      configurable: true,
      value: () => ({ matches: false }),
    })
    expect(isLikelyMobileUA()).toBe(false)

    if (originalMatchMedia) {
      Object.defineProperty(window, 'matchMedia', {
        configurable: true,
        value: originalMatchMedia,
      })
    } else {
      delete (window as any).matchMedia
    }

    expect(fmt(1024 * 1024 * 1024)).toBe('1.0 GB')
  })

  it('formats sizes and exposes no-resume upload helpers', async () => {
    expect(fmt(0)).toBe('0 B')
    expect(fmt(1536)).toBe('1.5 KB')

    const storage = createNoResumeStorage()
    await expect(storage.addUpload({})).resolves.toBeUndefined()
    await expect(storage.removeUpload({})).resolves.toBeUndefined()
    await expect(storage.listUploads()).resolves.toEqual([])
    await expect(storage.findUploadsByFingerprint('abc')).resolves.toEqual([])
    await expect(createNoResumeFingerprint()).resolves.toMatch(/^noresume-/)
  })

  it('returns false when matchMedia is unavailable and normalizes non-array rows', () => {
    const originalMatchMedia = window.matchMedia
    Object.defineProperty(window, 'matchMedia', {
      configurable: true,
      value: undefined,
    })
    expect(isLikelyMobileUA()).toBe(false)
    Object.defineProperty(window, 'matchMedia', { configurable: true, value: originalMatchMedia })
    expect(normalizeRows('bad input' as any)).toEqual([])
  })
})
