// One-time migration: prefixes any product/store image key that's missing
// its folder (i.e. was uploaded before the folder-prefix feature existed)
// with the appropriate folder, so every stored key follows the same
// "folder/filename" shape going forward. Safe to re-run — keys that
// already start with the folder (or any other folder) are left alone.
//
// For each key being moved to a new path, this also makes sure the new
// path actually resolves in S3:
//  - if the object still exists at its old (un-prefixed) key, it's copied
//    to the new folder-prefixed key
//  - if the object is missing entirely (confirmed to be the case for the
//    large majority of pre-existing rows — the original files are gone),
//    a generated "image unavailable" placeholder is uploaded to the new
//    key instead, so the stored URL never points at a dead link
import "../config/env";
import mongoose from "mongoose";
import sharp from "sharp";
import { databaseConnect, databaseDisconnect } from "../database/functions";
import { s3Bucket } from "../service/s3Bucket";

const BUCKET = process.env.AWS_S3_BUCKET_NAME!;
const PRODUCTS_FOLDER = process.env.AWS_S3_PRODUCTS_FOLDER;
const STORES_FOLDER = process.env.AWS_S3_STORES_FOLDER;

// A key already "has a folder" if it contains a "/" at all — don't double
// -prefix something that's in some other folder for a reason we don't know.
const hasFolder = (key: string) => key.includes("/");

async function objectExists(key: string): Promise<boolean> {
  try {
    await s3Bucket.headObject({ Bucket: BUCKET, Key: key }).promise();
    return true;
  } catch {
    return false;
  }
}

let placeholderBuffer: Buffer | null = null;
async function getPlaceholderBuffer(): Promise<Buffer> {
  if (placeholderBuffer) return placeholderBuffer;
  const svg = `
    <svg width="800" height="800" xmlns="http://www.w3.org/2000/svg">
      <rect width="100%" height="100%" fill="#e8e8e8"/>
      <text x="50%" y="48%" font-family="Arial, sans-serif" font-size="36"
        fill="#9a9a9a" text-anchor="middle">Image unavailable</text>
      <text x="50%" y="56%" font-family="Arial, sans-serif" font-size="22"
        fill="#b5b5b5" text-anchor="middle">Before the Dates</text>
    </svg>`;
  placeholderBuffer = await sharp(Buffer.from(svg)).jpeg({ quality: 80 }).toBuffer();
  return placeholderBuffer;
}

async function ensureObjectAt(newKey: string, oldKey: string): Promise<void> {
  if (await objectExists(newKey)) return; // already there, nothing to do

  if (await objectExists(oldKey)) {
    await s3Bucket
      .copyObject({
        Bucket: BUCKET,
        CopySource: `${BUCKET}/${encodeURIComponent(oldKey)}`,
        Key: newKey,
      })
      .promise();
    console.log(`  copied existing object: ${oldKey} -> ${newKey}`);
    return;
  }

  const body = await getPlaceholderBuffer();
  await s3Bucket
    .upload({ Bucket: BUCKET, Key: newKey, Body: body, ContentType: "image/jpeg" })
    .promise();
  console.log(`  missing in S3, uploaded placeholder at: ${newKey}`);
}

async function migrate() {
  if (!PRODUCTS_FOLDER || !STORES_FOLDER) {
    console.error(
      "AWS_S3_PRODUCTS_FOLDER / AWS_S3_STORES_FOLDER not set in .env",
    );
    process.exit(1);
  }

  await databaseConnect();
  console.log("Connected to MongoDB");

  // ---- products.images ----
  const products = mongoose.connection.collection("products");
  const productDocs = await products
    .find({ images: { $exists: true, $ne: [] } })
    .toArray();
  console.log(`Products scanned: ${productDocs.length}`);

  let productsUpdated = 0;
  for (const p of productDocs) {
    const images: unknown[] = Array.isArray(p.images) ? p.images : [];
    const needsWork = images.some(
      (img) => typeof img === "string" && !hasFolder(img),
    );
    if (!needsWork) continue;

    const newImages: string[] = [];
    for (const img of images) {
      if (typeof img !== "string" || hasFolder(img)) {
        newImages.push(img as string);
        continue;
      }
      const newKey = `${PRODUCTS_FOLDER}/${img}`;
      await ensureObjectAt(newKey, img);
      newImages.push(newKey);
    }

    await products.updateOne(
      { _id: p._id },
      { $set: { images: newImages } },
    );
    productsUpdated++;
  }
  console.log(`Products updated: ${productsUpdated}`);

  // ---- storeInformations.storeImage ----
  const stores = mongoose.connection.collection("storeinformations");
  const storeDocs = await stores
    .find({ storeImage: { $exists: true, $ne: "" } })
    .toArray();
  console.log(`Store info docs scanned: ${storeDocs.length}`);

  let storesUpdated = 0;
  for (const s of storeDocs) {
    if (typeof s.storeImage !== "string" || hasFolder(s.storeImage)) continue;

    const newKey = `${STORES_FOLDER}/${s.storeImage}`;
    await ensureObjectAt(newKey, s.storeImage);

    await stores.updateOne(
      { _id: s._id },
      { $set: { storeImage: newKey } },
    );
    storesUpdated++;
  }
  console.log(`Store info docs updated: ${storesUpdated}`);

  await databaseDisconnect();
}

migrate().catch((err) => {
  console.error("Migration failed:", err);
  process.exit(1);
});
