const { body, validationResult } = require("express-validator");
const DOMPurify = require("dompurify");
const { JSDOM } = require("jsdom");

// Create a DOM purify instance for server-side sanitization
const window = new JSDOM("").window;
const purify = DOMPurify(window);

// Password strength validation
const passwordStrengthRegex =
  /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z])(?=.*[-@$!%*?&#^]).{8,}$/; //-@$!%*?&#^
const commonPasswords = [
  "password",
  "123456",
  "123456789",
  "qwerty",
  "abc123",
  "password123",
  "admin",
  "letmein",
  "welcome",
  "monkey",
  "1234567890",
];

// Sanitization middleware
const sanitizeInput = (req, res, next) => {
  const sanitizeValue = (value) => {
    if (typeof value === "string") {
      return purify.sanitize(value, { ALLOWED_TAGS: [] });
    }
    if (typeof value === "object" && value !== null) {
      const sanitized = {};
      for (const [key, val] of Object.entries(value)) {
        sanitized[key] = sanitizeValue(val);
      }
      return sanitized;
    }
    return value;
  };

  if (req.body) {
    req.body = sanitizeValue(req.body);
  }
  if (req.query) {
    req.query = sanitizeValue(req.query);
  }
  if (req.params) {
    req.params = sanitizeValue(req.params);
  }

  next();
};

// Validation error handler
const handleValidationErrors = (req, res, next) => {
  const errors = validationResult(req);
  if (!errors.isEmpty()) {
    return res.status(400).json({
      error: "Validation failed",
      details: errors.array().map((err) => ({
        field: err.path,
        message: err.msg,
      })),
    });
  }
  next();
};

// Registration validation rules
const validateRegistration = [
  body("email")
    .isEmail()
    .normalizeEmail()
    .withMessage("Please provide a valid email address")
    .isLength({ max: 255 })
    .withMessage("Email must be less than 255 characters"),

  body("password")
    .isLength({ min: 8, max: 128 })
    .withMessage("Password must be between 8 and 128 characters")
    .matches(passwordStrengthRegex)
    .withMessage(
      "Password does not meet requirements"
    )
    .custom((value) => {
      if (commonPasswords.includes(value.toLowerCase())) {
        throw new Error(
          "Password is too common. Please choose a stronger password"
        );
      }
      return true;
    }),

  body("firstName")
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage("First name must be between 1 and 50 characters")
    .matches(/^[a-zA-Z\s\-']+$/)
    .withMessage(
      "First name can only contain letters, spaces, hyphens, and apostrophes"
    ),

  body("lastName")
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage("Last name must be between 1 and 50 characters")
    .matches(/^[a-zA-Z\s\-']+$/)
    .withMessage(
      "Last name can only contain letters, spaces, hyphens, and apostrophes"
    ),

  body("phone")
    .optional()
    .isMobilePhone()
    .withMessage("Please provide a valid phone number"),

  handleValidationErrors,
];

// Login validation rules
const validateLogin = [
  body("email")
    .isEmail()
    .normalizeEmail()
    .withMessage("Please provide a valid email address"),

  body("password")
    .notEmpty()
    .withMessage("Password is required")
    .isLength({ max: 128 })
    .withMessage("Password is too long"),

  handleValidationErrors,
];

// Google auth validation
const validateGoogleAuth = [
  body("code")
    .notEmpty()
    .withMessage("Authorization code is required")
    .isLength({ max: 512 })
    .withMessage("Invalid authorization code format"),

  handleValidationErrors,
];

// Profile update validation
const validateProfileUpdate = [
  body("firstName")
    .optional()
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage("First name must be between 1 and 50 characters")
    .matches(/^[a-zA-Z\s\-']+$/)
    .withMessage(
      "First name can only contain letters, spaces, hyphens, and apostrophes"
    ),

  body("lastName")
    .optional()
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage("Last name must be between 1 and 50 characters")
    .matches(/^[a-zA-Z\s\-']+$/)
    .withMessage(
      "Last name can only contain letters, spaces, hyphens, and apostrophes"
    ),

  body("phone")
    .optional()
    .isMobilePhone()
    .withMessage("Please provide a valid phone number"),

  body("address1")
    .optional()
    .trim()
    .isLength({ max: 255 })
    .withMessage("Address line 1 must be less than 255 characters"),

  body("address2")
    .optional()
    .trim()
    .isLength({ max: 255 })
    .withMessage("Address line 2 must be less than 255 characters"),

  body("city")
    .optional()
    .trim()
    .isLength({ max: 100 })
    .withMessage("City must be less than 100 characters")
    .matches(/^[a-zA-Z\s\-']+$/)
    .withMessage(
      "City can only contain letters, spaces, hyphens, and apostrophes"
    ),

  body("state")
    .optional()
    .trim()
    .isLength({ max: 100 })
    .withMessage("State must be less than 100 characters"),

  body("zipCode")
    .optional()
    .trim()
    .matches(/^[0-9]{5}(-[0-9]{4})?$/)
    .withMessage("Please provide a valid ZIP code"),

  body("country")
    .optional()
    .trim()
    .isLength({ max: 100 })
    .withMessage("Country must be less than 100 characters"),

  handleValidationErrors,
];

// Password change validation
const validatePasswordChange = [
  body("currentPassword")
    .notEmpty()
    .withMessage("Current password is required"),

  body("newPassword")
    .isLength({ min: 8, max: 128 })
    .withMessage("New password must be between 8 and 128 characters")
    .matches(passwordStrengthRegex)
    .withMessage(
      "New password must contain at least one uppercase letter, one lowercase letter, one number, and one special character"
    )
    .custom((value, { req }) => {
      if (value === req.body.currentPassword) {
        throw new Error("New password must be different from current password");
      }
      if (commonPasswords.includes(value.toLowerCase())) {
        throw new Error(
          "Password is too common. Please choose a stronger password"
        );
      }
      return true;
    }),

  body("confirmPassword").custom((value, { req }) => {
    if (value !== req.body.newPassword) {
      throw new Error("Password confirmation does not match");
    }
    return true;
  }),

  handleValidationErrors,
];

// Forgot password validation
const validateForgotPassword = [
  body("email")
    .isEmail()
    .normalizeEmail()
    .withMessage("Please provide a valid email address")
    .isLength({ max: 255 })
    .withMessage("Email must be less than 255 characters"),

  handleValidationErrors,
];

// Reset password validation
const validateResetPassword = [
  body("token")
    .notEmpty()
    .withMessage("Reset token is required")
    .isLength({ min: 64, max: 64 })
    .withMessage("Invalid reset token format"),

  body("newPassword")
    .isLength({ min: 8, max: 128 })
    .withMessage("Password must be between 8 and 128 characters")
    .matches(passwordStrengthRegex)
    .withMessage(
      "Password does not meet requirements"
    )
    .custom((value) => {
      if (commonPasswords.includes(value.toLowerCase())) {
        throw new Error(
          "Password is too common. Please choose a stronger password"
        );
      }
      return true;
    }),

  handleValidationErrors,
];

// Verify reset token validation
const validateVerifyResetToken = [
  body("token")
    .notEmpty()
    .withMessage("Reset token is required")
    .isLength({ min: 64, max: 64 })
    .withMessage("Invalid reset token format"),

  handleValidationErrors,
];

// Feedback validation
const validateFeedback = [
  body("feedbackText")
    .trim()
    .isLength({ min: 5, max: 5000 })
    .withMessage("Feedback must be between 5 and 5000 characters"),

  body("url")
    .optional()
    .trim()
    .isLength({ max: 500 })
    .withMessage("URL must be less than 500 characters"),

  handleValidationErrors,
];

module.exports = {
  sanitizeInput,
  handleValidationErrors,
  validateRegistration,
  validateLogin,
  validateGoogleAuth,
  validateProfileUpdate,
  validatePasswordChange,
  validateForgotPassword,
  validateResetPassword,
  validateVerifyResetToken,
  validateFeedback,
};
