const path = require('node:path');

const multer = require('multer');

const logger = require('./logger');

// Upload configuration constants
const MAX_FILE_SIZE = 72 * 1024 * 1024; // 72MB
const UPLOAD_TIMEOUT = 5 * 60 * 1000; // 5 minutes for large files

const uploadFolders = {
  selfieImg: 'public/host-uploads',
  supportingDocs: 'public/host-uploads',
  listingImgs: 'public/host-uploads',
  profileImg: 'public/user-uploads',
  validIdImg: 'public/user-uploads',
  messageAttachments: 'public/convo-uploads',
  travelersPhoto: 'public/user-uploads',
  governmentId: 'public/user-uploads',
};

const fileStorage = multer.diskStorage({
  destination: (req, file, cb) => {
    const uploadFolder = uploadFolders[file.fieldname];

    if (uploadFolder) {
      cb(null, uploadFolder);
    } else {
      cb(new Error('Invalid fieldname'), false);
    }
  },
  filename: (req, file, cb) => {
    const date = new Date();
    const formattedDate = `${date.toISOString().replace(/:/g, '-')}-`;
    cb(null, formattedDate + file.originalname);
  },
});

const allowedExtensions = ['.jpg', '.jpeg', '.png', '.webp', '.pdf', '.docx'];
const allowedMimeTypes = [
  'image/png',
  'image/jpg',
  'image/jpeg',
  'image/webp',
  'application/vnd.openxmlformats-officedocument.wordprocessingml.document', // DOCX
  'application/pdf',
];

const fileFilter = (req, file, cb) => {
  if (allowedMimeTypes.includes(file.mimetype)) {
    cb(null, true);
  } else if (file.mimetype === 'application/octet-stream') {
    const ext = path.extname(file.originalname).toLowerCase();
    if (allowedExtensions.includes(ext)) {
      cb(null, true);
    } else {
      logger.warn(
        `File format not supported (octet-stream): originalname=${file.originalname}, ext=${ext}, fieldname=${file.fieldname}${JSON.stringify(
          {
            originalname: file.originalname,
            ext,
            fieldname: file.fieldname,
            mimetype: file.mimetype,
          },
        )}`,
      );
      cb(
        new Error(
          `File format not supported (octet-stream): originalname=${file.originalname}, ext=${ext}, fieldname=${file.fieldname}`,
        ),
        false,
      );
    }
  } else {
    logger.error(
      `File format not supported (octet-stream): originalname=${file.originalname}, fieldname=${file.fieldname}${JSON.stringify(
        {
          originalname: file.originalname,
          fieldname: file.fieldname,
          mimetype: file.mimetype,
        },
      )}`,
    );
    cb(
      new Error(
        `File format not supported: originalname=${file.originalname}, mimetype=${file.mimetype}, fieldname=${file.fieldname}`,
      ),
      false,
    );
  }
};

/**
 * Enhanced multer error handler
 *
 * @param {Error} err The multer error
 * @returns {object} Categorized error response
 */
function handleMulterError(err) {
  if (err instanceof multer.MulterError) {
    switch (err.code) {
      case 'LIMIT_FILE_SIZE':
        return {
          message: `File too large. Maximum file size is ${MAX_FILE_SIZE / (1024 * 1024)}MB.`,
          type: 'FILE_TOO_LARGE',
          maxSize: MAX_FILE_SIZE,
          canRetry: false,
        };
      case 'LIMIT_FILE_COUNT':
        return {
          message: 'Too many files uploaded. Please reduce the number of files.',
          type: 'TOO_MANY_FILES',
          canRetry: false,
        };
      case 'LIMIT_UNEXPECTED_FILE':
        return {
          message: `Unexpected file field: ${err.field}. Please check your upload form.`,
          type: 'INVALID_FIELD',
          field: err.field,
          canRetry: false,
        };
      default:
        return {
          message: `Upload error: ${err.message}`,
          type: 'UPLOAD_ERROR',
          canRetry: true,
        };
    }
  }

  // Custom file filter errors
  if (err.message.includes('File format not supported')) {
    return {
      message: err.message,
      type: 'INVALID_FILE_TYPE',
      allowedTypes: allowedExtensions.join(', '),
      canRetry: false,
    };
  }

  if (err.message.includes('Invalid fieldname')) {
    return {
      message: 'Invalid upload field. Please check your form.',
      type: 'INVALID_FIELD',
      canRetry: false,
    };
  }

  // Network/timeout errors
  if (err.code === 'ETIMEDOUT' || err.message.includes('timeout')) {
    return {
      message: 'Upload timed out. Please check your internet connection and try again.',
      type: 'TIMEOUT',
      canRetry: true,
    };
  }

  // Generic error
  return {
    message: err.message || 'File upload failed. Please try again.',
    type: 'UPLOAD_ERROR',
    canRetry: true,
  };
}

const multerConfig = (req, res, next) => {
  // Set upload timeout
  req.setTimeout(UPLOAD_TIMEOUT);

  const upload = multer({
    storage: fileStorage,
    fileFilter,
    limits: {
      fileSize: MAX_FILE_SIZE,
      files: 30, // Max total files in single request
    },
  }).fields([
    {
      name: 'selfieImg',
      maxCount: 1,
    },
    {
      name: 'supportingDocs',
      maxCount: 20,
    },
    {
      name: 'listingImgs',
      maxCount: 20,
    },
    {
      name: 'profileImg',
      maxCount: 1,
    },
    {
      name: 'validIdImg',
      maxCount: 1,
    },
    {
      name: 'messageAttachments',
      maxCount: 10,
    },
    {
      name: 'travelersPhoto',
      maxCount: 1,
    },
    {
      name: 'governmentId',
      maxCount: 1,
    },
  ]);

  upload(req, res, (err) => {
    if (err) {
      const errorInfo = handleMulterError(err);

      logger.error('File upload error:', {
        errorType: errorInfo.type,
        message: errorInfo.message,
        originalError: err.message,
        field: err.field,
        files: req.files ? Object.keys(req.files) : [],
      });

      // Attach error info to request for downstream handling
      req.uploadError = errorInfo;

      // Don't block the request - let route handlers decide how to handle
      // Some routes might want to continue even with upload errors
      next();
      return;
    }

    // Log successful uploads
    if (req.files && Object.keys(req.files).length > 0) {
      const uploadedFiles = Object.entries(req.files).map(([field, files]) => ({
        field,
        count: files.length,
        totalSize: files.reduce((sum, f) => sum + f.size, 0),
      }));

      logger.info('Files uploaded successfully:', uploadedFiles);
    }

    next();
  });
};

module.exports = multerConfig;
