// One-off migration: copy every file under STORAGE_DIR (local disk) into the
// DigitalOcean Spaces bucket, preserving the exact key path so existing
// /api/files/<key> URLs in the database keep resolving after the switch.
//
// Usage (from the project root):
//   node scripts/migrate-storage-to-spaces.mjs            # copy missing files
//   FORCE=1 node scripts/migrate-storage-to-spaces.mjs    # overwrite existing
//
// Run it wherever the source files live (a machine/container with STORAGE_DIR
// populated and the SPACES_* env vars configured).
import { config } from "dotenv"
config({ path: ".env.local" })
import { promises as fs } from "fs"
import path from "path"
import {
  S3Client,
  PutObjectCommand,
  HeadObjectCommand,
} from "@aws-sdk/client-s3"

const STORAGE_DIR = path.resolve(process.cwd(), process.env.STORAGE_DIR ?? "./storage")
const bucket = process.env.SPACES_BUCKET
const force = process.env.FORCE === "1"

if (!process.env.SPACES_KEY || !process.env.SPACES_SECRET || !bucket) {
  console.error("Missing SPACES_KEY / SPACES_SECRET / SPACES_BUCKET — aborting.")
  process.exit(1)
}

const MIME = {
  pdf: "application/pdf", png: "image/png", jpg: "image/jpeg", jpeg: "image/jpeg",
  gif: "image/gif", webp: "image/webp", doc: "application/msword",
  docx: "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
  xls: "application/vnd.ms-excel",
  xlsx: "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
  csv: "text/csv", txt: "text/plain",
}
const ctype = (k) => MIME[k.split(".").pop()?.toLowerCase() ?? ""] ?? "application/octet-stream"

const s3 = new S3Client({
  region: process.env.SPACES_REGION || "us-east-1",
  endpoint: process.env.SPACES_ENDPOINT,
  forcePathStyle: false,
  credentials: { accessKeyId: process.env.SPACES_KEY, secretAccessKey: process.env.SPACES_SECRET },
})

async function* walk(dir) {
  let entries
  try {
    entries = await fs.readdir(dir, { withFileTypes: true })
  } catch {
    return
  }
  for (const e of entries) {
    const full = path.join(dir, e.name)
    if (e.isDirectory()) yield* walk(full)
    else if (e.isFile()) yield full
  }
}

async function existsInBucket(key) {
  try {
    await s3.send(new HeadObjectCommand({ Bucket: bucket, Key: key }))
    return true
  } catch {
    return false
  }
}

let migrated = 0, skipped = 0, failed = 0
console.log(`Source: ${STORAGE_DIR}`)
console.log(`Target: ${process.env.SPACES_ENDPOINT}/${bucket}${force ? "  (FORCE overwrite)" : ""}\n`)

for await (const file of walk(STORAGE_DIR)) {
  const key = path.relative(STORAGE_DIR, file).split(path.sep).join("/")
  try {
    if (!force && (await existsInBucket(key))) {
      console.log(`skip   ${key}`)
      skipped++
      continue
    }
    const body = await fs.readFile(file)
    await s3.send(new PutObjectCommand({
      Bucket: bucket, Key: key, Body: body, ContentType: ctype(key), ACL: "private",
    }))
    console.log(`upload ${key} (${body.length} bytes)`)
    migrated++
  } catch (e) {
    console.error(`FAIL   ${key} -> ${e.name}: ${e.message}`)
    failed++
  }
}

console.log(`\nDone. migrated=${migrated} skipped=${skipped} failed=${failed}`)
process.exit(failed > 0 ? 1 : 0)
