const sharp = require("sharp");
const exifReader = require("exif-reader");
const { logger } = require("../shared");

/**
 * Extract metadata from an image buffer
 * @param {Buffer} buffer - Image buffer
 * @returns {Object} Extracted metadata
 */
async function extractMetadata(buffer) {
  const image = sharp(buffer);
  const metadata = await image.metadata();

  let exifData = {};
  if (metadata.exif) {
    try {
      exifData = exifReader(metadata.exif);
    } catch (e) {
      // EXIF parsing failed, continue without it
      logger.warn("Failed to parse EXIF data", { error: e.message });
    }
  }

  return {
    // GPS data
    latitude: parseGpsCoordinate(
      exifData?.gps?.GPSLatitude,
      exifData?.gps?.GPSLatitudeRef
    ),
    longitude: parseGpsCoordinate(
      exifData?.gps?.GPSLongitude,
      exifData?.gps?.GPSLongitudeRef
    ),

    // Camera info
    cameraMake: exifData?.image?.Make || null,
    cameraModel: exifData?.image?.Model || null,
    cameraSoftware: exifData?.image?.Software || null,

    // Date/time
    dateTaken: parseExifDate(
      exifData?.exif?.DateTimeOriginal || exifData?.image?.DateTime
    ),

    // Dimensions
    width: metadata.width,
    height: metadata.height,
    orientation: metadata.orientation || 1,

    // File info
    fileSize: buffer.length,
  };
}

/**
 * Strip all metadata from an image buffer
 * Uses sharp's rotate() which auto-orients AND strips EXIF
 * @param {Buffer} buffer - Image buffer
 * @param {string} format - Output format (default: 'jpeg')
 * @returns {Buffer} Processed image buffer
 */
async function stripMetadata(buffer, format = "jpeg") {
  const image = sharp(buffer);
  const metadata = await image.metadata();

  // Handle different formats
  let processed;
  if (metadata.format === "gif") {
    // For GIFs, try to preserve animation but strip metadata
    processed = await image
      .gif()
      .toBuffer();
  } else if (metadata.format === "png") {
    // For PNGs, rotate strips metadata and we output as PNG
    processed = await image
      .rotate() // Auto-orient and strip EXIF
      .png()
      .toBuffer();
  } else if (metadata.format === "webp") {
    processed = await image
      .rotate()
      .webp()
      .toBuffer();
  } else {
    // Default to JPEG for best compatibility
    processed = await image
      .rotate() // Auto-orient and strip EXIF
      .jpeg({ quality: 90 })
      .toBuffer();
  }

  return processed;
}

/**
 * Convert GPS DMS (degrees, minutes, seconds) to decimal
 * @param {Array} dms - [degrees, minutes, seconds]
 * @param {string} ref - N/S/E/W reference
 * @returns {number|null} Decimal coordinate
 */
function parseGpsCoordinate(dms, ref) {
  if (!dms || !Array.isArray(dms) || dms.length !== 3) {
    return null;
  }

  const [degrees, minutes, seconds] = dms;
  let decimal = degrees + minutes / 60 + seconds / 3600;

  // South and West are negative
  if (ref === "S" || ref === "W") {
    decimal = -decimal;
  }

  // Round to 8 decimal places (about 1mm precision)
  return Math.round(decimal * 100000000) / 100000000;
}

/**
 * Parse EXIF date string to ISO date
 * EXIF format: "YYYY:MM:DD HH:MM:SS"
 * @param {string|Date} dateStr - EXIF date string or Date object
 * @returns {Date|null} Parsed date
 */
function parseExifDate(dateStr) {
  if (!dateStr) {
    return null;
  }

  // If already a Date object
  if (dateStr instanceof Date) {
    return dateStr;
  }

  // EXIF format: "YYYY:MM:DD HH:MM:SS"
  const match = String(dateStr).match(
    /(\d{4}):(\d{2}):(\d{2}) (\d{2}):(\d{2}):(\d{2})/
  );
  if (match) {
    const [, year, month, day, hour, minute, second] = match;
    return new Date(`${year}-${month}-${day}T${hour}:${minute}:${second}`);
  }

  // Try parsing as ISO date
  const date = new Date(dateStr);
  return isNaN(date.getTime()) ? null : date;
}

/**
 * Get the MIME type for a format
 * @param {string} format - Image format
 * @returns {string} MIME type
 */
function getMimeType(format) {
  const mimeTypes = {
    jpeg: "image/jpeg",
    jpg: "image/jpeg",
    png: "image/png",
    gif: "image/gif",
    webp: "image/webp",
  };
  return mimeTypes[format] || "image/jpeg";
}

module.exports = {
  extractMetadata,
  stripMetadata,
  parseGpsCoordinate,
  parseExifDate,
  getMimeType,
};
