import crypto from 'node:crypto'
import fs from 'node:fs'
import path from 'node:path'

const UPLOADS_DIR = path.join(process.cwd(), 'uploads')
const CACHE_DIR = path.join(UPLOADS_DIR, '.cache')
const VALID_WIDTHS = [320, 640, 1024, 1600]
const SUPPORTED_FORMATS = new Set(['avif', 'webp'])

/**
 * Find the original file by UUID (without extension) in the uploads directory tree.
 * Searches both /uploads/ and /uploads/reviews/.
 * Returns full path or null.
 */
export async function findOriginalFile(uuid, subdir = '') {
  const searchDirs = subdir ? [subdir] : ['', 'reviews']
  for (const dir of searchDirs) {
    for (const ext of ['.png', '.jpg', '.jpeg', '.webp']) {
      const fullPath = path.join(UPLOADS_DIR, dir, `${uuid}${ext}`)
      try {
        await fs.promises.access(fullPath)
        return fullPath
      } catch {
        // file not found with this extension, try next
      }
    }
  }
  return null
}

/**
 * Get or generate a resized image. Returns { path: string, isNew: boolean }.
 */
export async function getOrCreateResized(uuid, width, format, subdir = '') {
  const cacheSubdir = subdir ? subdir : ''
  const cacheFileName = `${uuid}_w${width}.${format}`
  const cachePath = path.join(CACHE_DIR, cacheSubdir, cacheFileName)

  try {
    await fs.promises.access(cachePath)
    return { path: cachePath, isNew: false }
  } catch {
    // cache miss, need to generate
  }

  const originalPath = await findOriginalFile(uuid, subdir)
  if (!originalPath) {
    return null
  }

  await fs.promises.mkdir(path.dirname(cachePath), { recursive: true })

  const sharp = (await import('sharp')).default
  let pipeline = sharp(originalPath)

  if (width) {
    pipeline = pipeline.resize(width, null, { withoutEnlargement: true })
  }

  const options = format === 'avif' ? { quality: 75, effort: 4 } : { quality: 80 }
  await pipeline[format](options).toFile(cachePath)

  return { path: cachePath, isNew: true }
}

export { VALID_WIDTHS, SUPPORTED_FORMATS }

/**
 * Generate all resize widths in AVIF + WebP for eager processing.
 * @param {string} uuid - UUID without extension
 * @param {string} subdir - Subdirectory (e.g., 'reviews') or empty
 * @param {string} originalPath - Full path to the original file
 */
export async function generateAllSizes(uuid, subdir, originalPath) {
  const cacheSubdir = subdir ? subdir : ''
  const cacheDir = path.join(CACHE_DIR, cacheSubdir)
  await fs.promises.mkdir(cacheDir, { recursive: true })

  const sharp = (await import('sharp')).default
  const source = sharp(originalPath)

  for (const width of VALID_WIDTHS) {
    for (const format of SUPPORTED_FORMATS) {
      const cacheFileName = `${uuid}_w${width}.${format}`
      const cachePath = path.join(CACHE_DIR, cacheSubdir, cacheFileName)

      const pipeline = source.clone().resize(width, null, { withoutEnlargement: true })
      const options = format === 'avif' ? { quality: 75, effort: 4 } : { quality: 80 }
      await pipeline[format](options).toFile(cachePath)
    }
  }
}

/**
 * Convert original file to WebP and delete the source file.
 * @param {string} uuid - UUID without extension
 * @param {string} subdir - Subdirectory (e.g., 'reviews') or empty
 * @returns {string} New URL path like `/uploads/<uuid>.webp`
 */
export async function convertOriginalToWebp(uuid, subdir) {
  const targetDir = subdir ? path.join(UPLOADS_DIR, subdir) : UPLOADS_DIR

  const originalPath = await findOriginalFile(uuid, subdir)
  if (!originalPath) {
    throw new Error(`Original file not found for UUID: ${uuid}`)
  }

  const originalExt = path.extname(originalPath).toLowerCase()
  const webpPath = path.join(targetDir, `${uuid}.webp`)

  const sharp = (await import('sharp')).default
  await sharp(originalPath).webp({ quality: 80 }).toFile(webpPath)

  if (originalExt !== '.webp') {
    await fs.promises.unlink(originalPath)
  }

  return subdir ? `/uploads/${subdir}/${uuid}.webp` : `/uploads/${uuid}.webp`
}
