const fs = require('fs/promises')
const path = require('path')
const {
  ensureData,
  closePool,
  updateImageAssetSrc,
  replaceUploadSourceReferences,
} = require('../src/db')

const BACKEND_ROOT = path.join(__dirname, '..')
const ASSETS_ROOT = path.join(BACKEND_ROOT, 'uploads', 'assets')
const FLAT_ASSET_PATTERN = /^\/uploads\/assets\/[^/]+$/

function getShardedAssetSrc(src) {
  const filename = path.basename(src || '')
  const shardDirectory = filename.slice(0, 2)
  if (!filename || shardDirectory.length < 2) return ''
  return `/uploads/assets/${shardDirectory}/${filename}`
}

async function moveAssetFile(fromSrc, toSrc) {
  const fromPath = path.join(BACKEND_ROOT, fromSrc.replace(/^\//, ''))
  const toPath = path.join(BACKEND_ROOT, toSrc.replace(/^\//, ''))
  await fs.mkdir(path.dirname(toPath), { recursive: true })

  try {
    await fs.rename(fromPath, toPath)
    return 'moved'
  } catch (error) {
    if (error?.code !== 'ENOENT') throw error
  }

  try {
    await fs.access(toPath)
    return 'already_moved'
  } catch (error) {
    if (error?.code === 'ENOENT') return 'missing'
    throw error
  }
}

async function main() {
  await ensureData()

  let dirEntries = []
  try {
    dirEntries = await fs.readdir(ASSETS_ROOT, { withFileTypes: true })
  } catch (error) {
    if (error?.code !== 'ENOENT') throw error
  }

  const flatAssets = dirEntries
    .filter((entry) => entry.isFile())
    .map((entry) => ({ src: `/uploads/assets/${entry.name}` }))
    .filter((asset) => FLAT_ASSET_PATTERN.test(asset.src || ''))
  const summary = {
    scanned: flatAssets.length,
    migrated: 0,
    alreadyMoved: 0,
    skipped: 0,
    missingFiles: 0,
    failed: 0,
    updatedRows: 0,
  }

  for (const asset of flatAssets) {
    const nextSrc = getShardedAssetSrc(asset.src)
    if (!nextSrc) {
      summary.skipped += 1
      continue
    }

    try {
      const moveStatus = await moveAssetFile(asset.src, nextSrc)
      if (moveStatus === 'missing') {
        summary.missingFiles += 1
        continue
      }

      await updateImageAssetSrc({ fromSrc: asset.src, toSrc: nextSrc })
      const replaced = await replaceUploadSourceReferences({ fromSrc: asset.src, toSrc: nextSrc })
      summary.updatedRows += Number(replaced.updatedRows || 0)

      if (moveStatus === 'already_moved') summary.alreadyMoved += 1
      else summary.migrated += 1
    } catch (error) {
      summary.failed += 1
      console.error('[migrate-flat-assets-to-sharded] failed:', asset.src, error?.message || error)
    }
  }

  console.log(JSON.stringify(summary, null, 2))
}

main()
  .catch((error) => {
    console.error(error)
    process.exitCode = 1
  })
  .finally(async () => {
    await closePool()
  })
