const {
  S3Client,
  GetObjectCommand,
  PutObjectCommand,
  DeleteObjectCommand,
} = require("@aws-sdk/client-s3");
const { extractMetadata, stripMetadata, getMimeType } = require("./imageProcessor");
const { saveImageMetadata, updateProcessingStatus } = require("./queries");
const { logger } = require("../shared");

const s3Client = new S3Client({ region: process.env.AWS_REGION });

/**
 * Lambda handler for S3 image processing.
 * Triggered by S3 ObjectCreated events on staging/ prefix.
 *
 * @param {Object} event - S3 event
 * @returns {Object} Processing results
 */
exports.handler = async (event) => {
  logger.info("Lambda invoked", { recordCount: event.Records?.length });

  const results = [];

  for (const record of event.Records) {
    const bucket = record.s3.bucket.name;
    const stagingKey = decodeURIComponent(record.s3.object.key.replace(/\+/g, " "));

    logger.info("Processing image", { bucket, stagingKey });

    try {
      // Only process files in staging/ folder
      if (!stagingKey.startsWith("staging/")) {
        logger.info("Skipping non-staging key", { stagingKey });
        results.push({ key: stagingKey, status: "skipped", reason: "not in staging" });
        continue;
      }

      // Calculate final key: staging/items/uuid.jpg -> items/uuid.jpg
      const finalKey = stagingKey.replace(/^staging\//, "");

      // Check if this is an image file
      if (!isImageFile(stagingKey)) {
        logger.info("Skipping non-image file", { stagingKey });
        results.push({ key: stagingKey, status: "skipped", reason: "not an image" });
        continue;
      }

      // Process the image
      await processImage(bucket, stagingKey, finalKey);

      results.push({ key: finalKey, status: "success" });
      logger.info("Successfully processed image", { finalKey });
    } catch (error) {
      logger.error("Error processing image", { stagingKey, error: error.message, stack: error.stack });
      results.push({ key: stagingKey, status: "error", error: error.message });

      // Try to update status to failed if we have a finalKey
      try {
        const finalKey = stagingKey.replace(/^staging\//, "");
        await updateProcessingStatus(finalKey, "failed", error.message);
      } catch (dbError) {
        logger.error("Failed to update error status in DB", { error: dbError.message });
      }
    }
  }

  return {
    statusCode: 200,
    body: JSON.stringify({ processed: results.length, results }),
  };
};

/**
 * Process a single image: extract metadata, strip, and move to final location.
 *
 * @param {string} bucket - S3 bucket name
 * @param {string} stagingKey - Staging key (e.g., staging/items/uuid.jpg)
 * @param {string} finalKey - Final key (e.g., items/uuid.jpg)
 */
async function processImage(bucket, stagingKey, finalKey) {
  // 1. Download image from staging location
  logger.debug("Downloading from staging", { stagingKey });
  const getCommand = new GetObjectCommand({
    Bucket: bucket,
    Key: stagingKey,
  });
  const response = await s3Client.send(getCommand);
  const buffer = Buffer.from(await response.Body.transformToByteArray());

  // 2. Extract metadata BEFORE stripping
  logger.debug("Extracting metadata");
  const metadata = await extractMetadata(buffer);
  logger.info("Extracted metadata", { finalKey, metadata });

  // 3. Save metadata to database
  logger.debug("Saving metadata to DB", { finalKey });
  await saveImageMetadata(finalKey, metadata);

  // 4. Strip metadata from image
  logger.debug("Stripping metadata");
  const strippedBuffer = await stripMetadata(buffer);

  // 5. Determine content type from original
  const format = stagingKey.split(".").pop().toLowerCase();
  const contentType = getMimeType(format);

  // 6. Upload clean image to FINAL location
  logger.debug("Uploading to final location", { finalKey });
  const putCommand = new PutObjectCommand({
    Bucket: bucket,
    Key: finalKey,
    Body: strippedBuffer,
    ContentType: contentType,
    CacheControl: getCacheControl(finalKey),
    Metadata: {
      "x-processed": "true",
      "x-processed-at": new Date().toISOString(),
    },
  });
  await s3Client.send(putCommand);

  // 7. Delete staging file
  logger.debug("Deleting staging file", { stagingKey });
  const deleteCommand = new DeleteObjectCommand({
    Bucket: bucket,
    Key: stagingKey,
  });
  await s3Client.send(deleteCommand);

  // 8. Update processing status to completed
  logger.debug("Updating processing status to completed");
  await updateProcessingStatus(finalKey, "completed");
}

/**
 * Check if a file is an image based on extension.
 *
 * @param {string} key - S3 key
 * @returns {boolean}
 */
function isImageFile(key) {
  const imageExtensions = [".jpg", ".jpeg", ".png", ".gif", ".webp"];
  const ext = key.toLowerCase().slice(key.lastIndexOf("."));
  return imageExtensions.includes(ext);
}

/**
 * Get Cache-Control header based on folder.
 *
 * @param {string} key - S3 key
 * @returns {string}
 */
function getCacheControl(key) {
  // Private folders get shorter cache
  if (key.startsWith("messages/") || key.startsWith("condition-checks/")) {
    return "private, max-age=3600";
  }
  // Public folders get longer cache
  return "public, max-age=86400";
}
