import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'
import { afterAll, beforeAll, describe, expect, it } from 'vitest'
import { findOriginalFile, getOrCreateResized } from '../image-resize.js'

const TEST_DIR = path.join(process.cwd(), 'uploads', '.test-tmp')
const UPLOADS_DIR = path.join(process.cwd(), 'uploads')

beforeAll(async () => {
  await fs.promises.mkdir(TEST_DIR, { recursive: true })
  // Create a small test PNG file
  const sharp = (await import('sharp')).default
  const testPng = path.join(TEST_DIR, 'test-original.png')
  await sharp({
    create: { width: 100, height: 100, channels: 3, background: { r: 255, g: 0, b: 0 } },
  })
    .png()
    .toFile(testPng)
})

afterAll(async () => {
  await fs.promises.rm(TEST_DIR, { recursive: true, force: true })
  // Clean up any cache files created during tests
  const cacheDir = path.join(UPLOADS_DIR, '.cache')
  try {
    await fs.promises.rm(cacheDir, { recursive: true, force: true })
  } catch {
    // ignore
  }
})

describe('image-resize', () => {
  it('findOriginalFile locates file by UUID', async () => {
    const files = await fs.promises.readdir(TEST_DIR)
    const pngFile = files.find((f) => f.endsWith('.png'))
    const uuid = pngFile.replace('.png', '')

    // Copy file to actual uploads directory
    const destPath = path.join(UPLOADS_DIR, pngFile)
    await fs.promises.copyFile(path.join(TEST_DIR, pngFile), destPath)

    const found = await findOriginalFile(uuid)
    expect(found).not.toBeNull()
    expect(found).toBe(destPath)

    // Cleanup
    await fs.promises.unlink(destPath)
  })

  it('getOrCreateResized generates AVIF file', async () => {
    const sharp = (await import('sharp')).default
    const uuid = crypto.randomUUID()
    const testPath = path.join(UPLOADS_DIR, `${uuid}.png`)
    await sharp({
      create: { width: 200, height: 200, channels: 3, background: { r: 0, g: 255, b: 0 } },
    })
      .png()
      .toFile(testPath)

    const result = await getOrCreateResized(uuid, 100, 'avif')
    expect(result).not.toBeNull()
    expect(result.isNew).toBe(true)
    expect(result.path).toContain('.cache')
    expect(result.path).toContain('_w100.avif')

    const exists = await fs.promises.access(result.path).then(() => true).catch(() => false)
    expect(exists).toBe(true)

    // Verify it's actually AVIF (sharp reports AVIF as 'heif' in metadata)
    expect(result.path).toMatch(/\.avif$/)

    // Cleanup
    await fs.promises.unlink(testPath)
    await fs.promises.unlink(result.path)
  })

  it('getOrCreateResized returns cached file on second call', async () => {
    const sharp = (await import('sharp')).default
    const uuid = crypto.randomUUID()
    const testPath = path.join(UPLOADS_DIR, `${uuid}.png`)
    await sharp({
      create: { width: 200, height: 200, channels: 3, background: { r: 0, g: 0, b: 255 } },
    })
      .png()
      .toFile(testPath)

    const first = await getOrCreateResized(uuid, 100, 'webp')
    expect(first.isNew).toBe(true)

    const second = await getOrCreateResized(uuid, 100, 'webp')
    expect(second.isNew).toBe(false)
    expect(second.path).toBe(first.path)

    // Cleanup
    await fs.promises.unlink(testPath)
    await fs.promises.unlink(first.path)
  })
})

describe('eager image processing', () => {
  it('generateAllSizes creates all width+format combinations', async () => {
    const { generateAllSizes } = await import('../image-resize.js')
    const sharp = (await import('sharp')).default
    const uuid = 'test-eager-uuid-1'
    const testImagePath = path.join(UPLOADS_DIR, `${uuid}.png`)
    await sharp({ create: { width: 2000, height: 1500, channels: 3, background: { r: 255, g: 0, b: 0 } } })
      .png()
      .toFile(testImagePath)

    await generateAllSizes(uuid, '', testImagePath)

    const cacheDir = path.join(UPLOADS_DIR, '.cache')
    for (const width of [320, 640, 1024, 1600]) {
      for (const format of ['avif', 'webp']) {
        const cachePath = path.join(cacheDir, `${uuid}_w${width}.${format}`)
        const exists = await fs.promises.access(cachePath).then(() => true).catch(() => false)
        expect(exists).toBe(true)
      }
    }

    // Cleanup
    await fs.promises.unlink(testImagePath)
    for (const width of [320, 640, 1024, 1600]) {
      for (const format of ['avif', 'webp']) {
        const cachePath = path.join(cacheDir, `${uuid}_w${width}.${format}`)
        try {
          await fs.promises.unlink(cachePath)
        } catch {
          // ignore
        }
      }
    }
  })

  it('convertOriginalToWebp converts and deletes original', async () => {
    const { convertOriginalToWebp } = await import('../image-resize.js')
    const sharp = (await import('sharp')).default
    const uuid = 'test-eager-uuid-2'
    const testImagePath = path.join(UPLOADS_DIR, `${uuid}.png`)
    await sharp({ create: { width: 800, height: 600, channels: 3, background: { r: 0, g: 255, b: 0 } } })
      .png()
      .toFile(testImagePath)

    const result = await convertOriginalToWebp(uuid, '')

    expect(result).toBe(`/uploads/${uuid}.webp`)
    const pngExists = await fs.promises.access(testImagePath).then(() => true).catch(() => false)
    expect(pngExists).toBe(false)
    const webpPath = path.join(UPLOADS_DIR, `${uuid}.webp`)
    const webpExists = await fs.promises.access(webpPath).then(() => true).catch(() => false)
    expect(webpExists).toBe(true)

    // Cleanup
    try {
      await fs.promises.unlink(webpPath)
    } catch {
      // ignore
    }
  })
})
