import multer from 'multer';
import path from 'path';
import fs from 'fs';
import sharp from 'sharp';
import { fileURLToPath } from 'url';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
export const uploadsDir = path.join(__dirname, '../uploads');

/** Max stored dimensions (aspect ratio preserved, never upscaled). */
export const MAX_IMAGE_WIDTH = 1920;
export const MAX_IMAGE_HEIGHT = 1080;

/** Raster only — no SVG (stored XSS if served as image/*). */
export const ALLOWED_IMAGE_MIMES = new Set([
  'image/png',
  'image/jpeg',
  'image/jpg',
  'image/webp',
  'image/gif',
]);

export const ALLOWED_IMAGE_EXTS = new Set(['.png', '.jpg', '.jpeg', '.webp', '.gif']);

/** sharp `metadata().format` values we accept */
const ALLOWED_SHARP_FORMATS = new Set(['png', 'jpeg', 'jpg', 'webp', 'gif']);

if (!fs.existsSync(uploadsDir)) {
  fs.mkdirSync(uploadsDir, { recursive: true });
}

export function safeExt(originalname, mimetype = '') {
  const fromName = path.extname(originalname || '').toLowerCase();
  if (ALLOWED_IMAGE_EXTS.has(fromName)) return fromName === '.jpeg' ? '.jpg' : fromName;
  if (mimetype === 'image/png') return '.png';
  if (mimetype === 'image/webp') return '.webp';
  if (mimetype === 'image/gif') return '.gif';
  if (mimetype === 'image/jpeg' || mimetype === 'image/jpg') return '.jpg';
  return '.png';
}

function isAllowedUpload(file) {
  const mime = String(file?.mimetype || '').toLowerCase();
  const ext = path.extname(file?.originalname || '').toLowerCase();
  if (!ALLOWED_IMAGE_MIMES.has(mime)) return false;
  // If the client sent an extension, it must match the allowlist (blocks .svg renamed tricks loosely)
  if (ext && !ALLOWED_IMAGE_EXTS.has(ext)) return false;
  return true;
}

/**
 * Downscale an uploaded image if larger than 1920×1080, keeping aspect ratio.
 * Also rejects non-raster / spoofed files (e.g. SVG with a fake image/* type).
 * Overwrites the file in place. No-ops when already within bounds.
 */
export async function processUploadedImage(file) {
  if (!file?.path) return;

  const filePath = file.path;
  let meta;
  try {
    const animated = file.mimetype === 'image/gif';
    meta = await sharp(filePath, { animated, failOn: 'none' }).metadata();
  } catch {
    await fs.promises.unlink(filePath).catch(() => {});
    throw new Error('Invalid or unsupported image file');
  }

  const format = String(meta.format || '').toLowerCase();
  if (!ALLOWED_SHARP_FORMATS.has(format)) {
    await fs.promises.unlink(filePath).catch(() => {});
    throw new Error('Only PNG, JPEG, WebP, and GIF images are allowed');
  }

  let width = meta.width || 0;
  let height = meta.height || 0;
  if (!width || !height) return;

  // EXIF orientations 5–8 swap display width/height
  const orientation = meta.orientation || 1;
  if (orientation >= 5 && orientation <= 8) {
    [width, height] = [height, width];
  }
  if (width <= MAX_IMAGE_WIDTH && height <= MAX_IMAGE_HEIGHT) return;

  const tmpPath = `${filePath}.resized`;
  try {
    const animated = format === 'gif';
    await sharp(filePath, { animated, failOn: 'none' })
      .rotate() // bake EXIF orientation before resize
      .resize({
        width: MAX_IMAGE_WIDTH,
        height: MAX_IMAGE_HEIGHT,
        fit: 'inside',
        withoutEnlargement: true,
      })
      .toFile(tmpPath);
    await fs.promises.rename(tmpPath, filePath);
  } catch (err) {
    await fs.promises.unlink(tmpPath).catch(() => {});
    throw err;
  }
}

export function createUploader(prefix, { maxBytes = 3 * 1024 * 1024, filename } = {}) {
  const storage = multer.diskStorage({
    destination: (_req, _file, cb) => cb(null, uploadsDir),
    filename:
      filename ||
      ((_req, file, cb) => {
        cb(
          null,
          `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}${safeExt(file.originalname, file.mimetype)}`
        );
      }),
  });

  return multer({
    storage,
    limits: { fileSize: maxBytes },
    fileFilter: (_req, file, cb) => {
      if (!isAllowedUpload(file)) {
        return cb(new Error('Only PNG, JPEG, WebP, and GIF images are allowed'));
      }
      cb(null, true);
    },
  });
}
