import { readFile } from 'node:fs/promises'
import path from 'node:path'

import { saveBufferToObjectStorage, probeObjectStorageHealth } from '../src/lite/file-storage.mjs'
import { executeSql, queryJson, sqlLiteral } from '../src/lite/pg.mjs'

const limit = Math.max(1, Number(process.env.FILE_MIGRATION_LIMIT || process.argv[2] || 100))
const bucketRoot = process.env.FILE_BUCKET_DIR || path.resolve('storage/uploads')

function resolveLocalPath(storagePath = '', objectKey = '') {
  if (path.isAbsolute(storagePath)) return storagePath
  return path.join(bucketRoot, storagePath || objectKey)
}

async function fileBuffer(row) {
  if (row.contentBase64) return Buffer.from(row.contentBase64, 'base64')
  if (row.storagePath || row.objectKey) return readFile(resolveLocalPath(row.storagePath, row.objectKey))
  throw new Error('file has neither database content nor local storage path')
}

async function main() {
  const health = await probeObjectStorageHealth()
  if (health.status !== 'ok') {
    throw new Error(`object storage is not ready: ${health.message || health.code || health.status}`)
  }

  const rows = await queryJson(`
    select coalesce(json_agg(row_to_json(t)), '[]'::json)
    from (
      select
        id,
        file_name as "fileName",
        mime_type as "mimeType",
        file_size as "fileSize",
        content_base64 as "contentBase64",
        storage_driver as "storageDriver",
        object_key as "objectKey",
        storage_path as "storagePath"
      from uploaded_files
      where coalesce(storage_driver, 'database') <> 's3'
      order by created_at asc
      limit ${limit}
    ) t;
  `)

  let migrated = 0
  for (const row of rows || []) {
    try {
      const body = await fileBuffer(row)
      const stored = await saveBufferToObjectStorage(body, row.fileName, row.mimeType)
      await executeSql(`
        update uploaded_files
        set storage_driver = 's3',
            bucket = ${sqlLiteral(stored.bucket || '')},
            object_key = ${sqlLiteral(stored.objectKey)},
            storage_path = ${sqlLiteral(stored.storagePath)}
        where id = ${sqlLiteral(row.id)}::uuid;
      `)
      migrated += 1
      console.log(`migrated ${row.id} ${row.fileName} -> ${stored.storagePath}`)
    } catch (error) {
      console.warn(`skip ${row.id} ${row.fileName}: ${error.message}`)
    }
  }

  console.log(`uploaded file migration complete: ${migrated}/${rows?.length || 0}`)
}

main().catch((error) => {
  console.error(error.stack || error.message)
  process.exit(1)
})
