// src/middleware/multer.middleware.ts
import multer from 'multer';
import path from 'path';
import fs from 'node:fs/promises';
import { Request, Response, NextFunction } from 'express';
import { UserProfile } from '../types';
import { sanitizeFilename } from '../utils/stringUtils';
import { ValidationError } from '../services/db/errors.db';
import { logger } from '../services/logger.server';

export const flyerStoragePath =
  process.env.STORAGE_PATH || '/var/www/flyer-crawler.projectium.com/flyer-images';
export const avatarStoragePath = path.join(process.cwd(), 'public', 'uploads', 'avatars');

// Ensure directories exist at startup
(async () => {
  try {
    await fs.mkdir(flyerStoragePath, { recursive: true });
    await fs.mkdir(avatarStoragePath, { recursive: true });
    logger.info('Ensured multer storage directories exist.');
  } catch (error) {
    const err = error instanceof Error ? error : new Error(String(error));
    logger.error({ error: err }, 'Failed to create multer storage directories on startup.');
  }
})();

type StorageType = 'flyer' | 'avatar';

const getStorageConfig = (type: StorageType) => {
  switch (type) {
    case 'avatar':
      return multer.diskStorage({
        destination: (req, file, cb) => cb(null, avatarStoragePath),
        filename: (req, file, cb) => {
          const user = req.user as UserProfile | undefined;
          if (!user) {
            // This should ideally not happen if auth middleware runs first.
            return cb(new Error('User not authenticated for avatar upload'), '');
          }
          if (process.env.NODE_ENV === 'test') {
            // Use a predictable filename for test avatars for easy cleanup.
            return cb(null, `test-avatar${path.extname(file.originalname) || '.png'}`);
          }
          const uniqueSuffix = `${user.user.user_id}-${Date.now()}${path.extname(
            file.originalname,
          )}`;
          cb(null, uniqueSuffix);
        },
      });
    case 'flyer':
    default:
      return multer.diskStorage({
        destination: (req, file, cb) => cb(null, flyerStoragePath),
        filename: (req, file, cb) => {
          if (process.env.NODE_ENV === 'test') {
            // Use a predictable filename for test flyers for easy cleanup.
            const ext = path.extname(file.originalname);
            return cb(null, `${file.fieldname}-test-flyer-image${ext || '.jpg'}`);
          }
          const uniqueSuffix = `${Date.now()}-${Math.round(Math.random() * 1e9)}`;
          const sanitizedOriginalName = sanitizeFilename(file.originalname);
          cb(null, `${file.fieldname}-${uniqueSuffix}-${sanitizedOriginalName}`);
        },
      });
  }
};

const imageFileFilter = (req: Request, file: Express.Multer.File, cb: multer.FileFilterCallback) => {
  if (file.mimetype.startsWith('image/')) {
    cb(null, true);
  } else {
    // Reject the file with a specific error that can be caught by a middleware.
    const validationIssue = { path: ['file', file.fieldname], message: 'Only image files are allowed!' };
    const err = new ValidationError([validationIssue], 'Only image files are allowed!');
    cb(err as Error); // Cast to Error to satisfy multer's type, though ValidationError extends Error.
  }
};

interface MulterOptions {
  storageType: StorageType;
  fileSize?: number;
  fileFilter?: 'image';
}

/**
 * Creates a configured multer instance for file uploads.
 * @param options - Configuration for storage type, file size, and file filter.
 * @returns A multer instance.
 */
export const createUploadMiddleware = (options: MulterOptions) => {
  const multerOptions: multer.Options = {
    storage: getStorageConfig(options.storageType),
  };

  if (options.fileSize) {
    multerOptions.limits = { fileSize: options.fileSize };
  }

  if (options.fileFilter === 'image') {
    multerOptions.fileFilter = imageFileFilter;
  }

  return multer(multerOptions);
};

/**
 * A general error handler for multer. Place this after all routes using multer in your router file.
 * It catches errors from `fileFilter` and other multer issues (e.g., file size limits).
 */
export const handleMulterError = (
  err: Error,
  req: Request,
  res: Response,
  next: NextFunction,
) => {
  if (err instanceof multer.MulterError) {
    // A Multer error occurred when uploading (e.g., file too large).
    return res.status(400).json({ message: `File upload error: ${err.message}` });
  }
  // If it's not a multer error, pass it on.
  next(err);
};