import { Request, Response } from 'express';
import crypto from 'crypto';
import fs from 'fs/promises';
import path from 'path';
import { assetStorageDir, assetUrl } from '../config/assets';
import { sendSuccess, sendError } from '../utils/response.utils';

const maxImageBytes = 8 * 1024 * 1024;
const supportedImages: Record<string, string> = {
  'image/jpeg': '.jpg',
  'image/png': '.png',
  'image/webp': '.webp',
  'image/gif': '.gif',
  'image/avif': '.avif',
};

interface ImageUploadInput {
  dataUrl?: unknown;
  fileName?: unknown;
}

const saveImage = async (input: ImageUploadInput) => {
  if (typeof input.dataUrl !== 'string') throw new Error('An image file is required.');
  const match = input.dataUrl.match(/^data:(image\/[a-z0-9.+-]+);base64,([a-zA-Z0-9+/=\r\n]+)$/);
  if (!match) throw new Error('The image payload is invalid.');

  const mimeType = match[1].toLowerCase();
  const extension = supportedImages[mimeType];
  if (!extension) throw new Error('Use a JPEG, PNG, WebP, GIF, or AVIF image.');

  const bytes = Buffer.from(match[2], 'base64');
  if (bytes.length === 0) throw new Error('The image file is empty.');
  if (bytes.length > maxImageBytes) throw new Error('The image must be 8 MB or smaller.');

  const storageKey = `uploads/books/${crypto.randomUUID()}${extension}`;
  const destination = path.join(assetStorageDir, ...storageKey.split('/'));
  const temporary = `${destination}.part`;
  await fs.mkdir(path.dirname(destination), { recursive: true });
  await fs.writeFile(temporary, bytes, { flag: 'wx' });
  await fs.rename(temporary, destination);

  return {
    url: assetUrl(storageKey),
    key: storageKey,
    fileName: typeof input.fileName === 'string' ? input.fileName : undefined,
    size: bytes.length,
    mimeType,
  };
};

export const uploadSingleImage = async (req: Request, res: Response) => {
  try {
    const image = await saveImage(req.body ?? {});
    return sendSuccess(res, 'Image uploaded to local storage successfully', image);
  } catch (error: any) {
    return sendError(res, 'Image upload failed', error.message, 400);
  }
};

export const uploadMultipleImages = async (req: Request, res: Response) => {
  try {
    const images = Array.isArray(req.body?.images) ? req.body.images : [];
    if (images.length === 0) throw new Error('At least one image is required.');
    if (images.length > 8) throw new Error('Upload no more than 8 images at once.');

    const saved = await Promise.all(images.map((image: ImageUploadInput) => saveImage(image)));
    return sendSuccess(res, 'Images uploaded to local storage successfully', {
      images: saved,
      urls: saved.map((image) => image.url),
    });
  } catch (error: any) {
    return sendError(res, 'Multiple images upload failed', error.message, 400);
  }
};
