import { EventEmitter } from 'events'
import { processDownload } from '../processor'
import { prisma } from '@/lib/prisma'
import * as tokenLib from '@/lib/token'
import * as cp from 'child_process'

jest.mock('@/lib/prisma', () => ({
  prisma: {
    download: { findUnique: jest.fn(), update: jest.fn() },
  },
}))
jest.mock('@/lib/token')
jest.mock('child_process')
jest.mock('fs', () => ({
  readdirSync: jest.fn(() => ['uuid-abc.mp4']),
  statSync: jest.fn(() => ({ size: 1024 })),
}))

const mockFindUnique = prisma.download.findUnique as jest.Mock
const mockUpdate = prisma.download.update as jest.Mock
const mockCreateToken = tokenLib.createToken as jest.Mock
const mockSpawn = cp.spawn as jest.Mock

const baseDownload = {
  id: 'dl-1',
  uuid: 'uuid-abc',
  url: 'https://youtube.com/watch?v=abc',
  format: 'mp4',
  quality: 'best',
  subtitles: false,
  extraArgs: null,
}

function makeChildProcess(exitCode: number, stderrMsg = '') {
  const proc = new EventEmitter() as any
  proc.stdout = new EventEmitter()
  proc.stderr = new EventEmitter()
  setImmediate(() => {
    if (stderrMsg) proc.stderr.emit('data', Buffer.from(stderrMsg))
    proc.emit('close', exitCode)
  })
  return proc
}

describe('processDownload', () => {
  beforeEach(() => {
    jest.clearAllMocks()
    mockFindUnique.mockResolvedValue(baseDownload)
    mockUpdate.mockResolvedValue({})
    mockCreateToken.mockResolvedValue('new-token')
  })

  it('marks download as DONE on yt-dlp exit code 0', async () => {
    mockSpawn.mockReturnValue(makeChildProcess(0))
    await processDownload('dl-1')
    expect(mockUpdate).toHaveBeenCalledWith(
      expect.objectContaining({
        where: { id: 'dl-1' },
        data: expect.objectContaining({ status: 'DONE' }),
      })
    )
  })

  it('creates a token on success', async () => {
    mockSpawn.mockReturnValue(makeChildProcess(0))
    await processDownload('dl-1')
    expect(mockCreateToken).toHaveBeenCalledWith('dl-1')
  })

  it('marks download as FAILED on non-zero exit code', async () => {
    mockSpawn.mockReturnValue(makeChildProcess(1, 'unsupported URL'))
    await processDownload('dl-1')
    expect(mockUpdate).toHaveBeenCalledWith(
      expect.objectContaining({
        data: expect.objectContaining({
          status: 'FAILED',
          errorMsg: expect.stringContaining('unsupported URL'),
        }),
      })
    )
  })

  it('does nothing when download not found', async () => {
    mockFindUnique.mockResolvedValue(null)
    await expect(processDownload('nonexistent')).resolves.toBeUndefined()
    expect(mockSpawn).not.toHaveBeenCalled()
  })
})
