export const MAX_MEDIA_SIZE_MB = 5;
export const MAX_MEDIA_SIZE_BYTES = MAX_MEDIA_SIZE_MB * 1024 * 1024;

const COMPRESSION_STEPS = [
  { maxDimension: 2560, quality: 0.85 },
  { maxDimension: 1920, quality: 0.8 },
  { maxDimension: 1600, quality: 0.7 },
  { maxDimension: 1280, quality: 0.6 },
  { maxDimension: 1024, quality: 0.5 },
];

const toMb = (bytes) => (bytes / 1024 / 1024).toFixed(1);

export const isImageFile = (file) => Boolean(file?.type?.startsWith("image/"));

const loadImage = (file) =>
  new Promise((resolve) => {
    const objectUrl = URL.createObjectURL(file);
    const image = new Image();

    image.onload = () =>
      resolve({ image, release: () => URL.revokeObjectURL(objectUrl) });
    image.onerror = () => {
      URL.revokeObjectURL(objectUrl);
      resolve(null);
    };

    image.src = objectUrl;
  });

const drawToJpeg = (image, file, { maxDimension, quality }) =>
  new Promise((resolve) => {
    const scale = Math.min(1, maxDimension / Math.max(image.width, image.height));
    const canvas = document.createElement("canvas");
    canvas.width = Math.max(1, Math.round(image.width * scale));
    canvas.height = Math.max(1, Math.round(image.height * scale));

    const context = canvas.getContext("2d");
    if (!context) {
      resolve(null);
      return;
    }

    context.fillStyle = "#ffffff";
    context.fillRect(0, 0, canvas.width, canvas.height);
    context.drawImage(image, 0, 0, canvas.width, canvas.height);

    canvas.toBlob(
      (blob) => {
        if (!blob) {
          resolve(null);
          return;
        }

        const name = file.name.replace(/\.[^.]+$/, "") || "image";
        resolve(
          new File([blob], `${name}.jpg`, {
            type: "image/jpeg",
            lastModified: Date.now(),
          }),
        );
      },
      "image/jpeg",
      quality,
    );
  });

export const compressImageToLimit = async (
  file,
  maxSizeBytes = MAX_MEDIA_SIZE_BYTES,
) => {
  const loaded = await loadImage(file);
  if (!loaded) return null;

  try {
    for (const step of COMPRESSION_STEPS) {
      const compressed = await drawToJpeg(loaded.image, file, step);
      if (compressed && compressed.size <= maxSizeBytes) return compressed;
    }
  } finally {
    loaded.release();
  }

  return null;
};

export const prepareMediaFile = async (
  file,
  { maxSizeBytes = MAX_MEDIA_SIZE_BYTES } = {},
) => {
  if (!(file instanceof File)) {
    return { file: null, error: null, compressed: false };
  }

  if (file.size <= maxSizeBytes) {
    return { file, error: null, compressed: false };
  }

  const error = {
    size: toMb(file.size),
    max: toMb(maxSizeBytes),
  };

  if (!isImageFile(file)) {
    return { file: null, error: { ...error, code: "too_large" }, compressed: false };
  }

  const compressed = await compressImageToLimit(file, maxSizeBytes);

  if (!compressed) {
    return {
      file: null,
      error: { ...error, code: "image_too_large" },
      compressed: false,
    };
  }

  return { file: compressed, error: null, compressed: true };
};
