/**
 * Discount Validations
 * Validation rules for discount operations
 */

// Import express-validator
const { body, param, query, validationResult } = require('express-validator');
// Import custom error class
const { ValidationError } = require('../../../utils/errors');

/**
 * Validation middleware
 * Checks validation results and throws error if validation fails
 * @param {Object} req - Express request object
 * @param {Object} res - Express response object
 * @param {Function} next - Express next middleware
 */
const validate = (req, res, next) => {
  // Get validation results
  const errors = validationResult(req); // Get validation errors
  
  // If validation errors exist, format and throw error
  if (!errors.isEmpty()) {
    // Format errors
    const formattedErrors = errors.array().map((error) => ({
      field: error.path || error.param || error.location, // Field name
      message: error.msg, // Error message
      value: error.value, // Invalid value
    }));
    
    // Log validation errors for debugging
    const logger = require('../../../utils/logger');
    logger.error('Validation failed', {
      url: req.url,
      method: req.method,
      body: req.body,
      errors: formattedErrors,
    });
    
    // Throw validation error
    throw new ValidationError('Validation failed', formattedErrors); // Throw error
  }
  
  // Continue to next middleware
  next(); // Proceed
};

// ============================================
// Discount Validations
// ============================================

/**
 * Validation rules for creating discount
 */
const validateCreateDiscount = [
  // Validate name
  body('name')
    .notEmpty()
    .withMessage('Discount name is required')
    .trim()
    .isLength({ min: 1, max: 150 })
    .withMessage('Discount name must be between 1 and 150 characters'),
  
  // Validate code (optional)
  body('code')
    .optional()
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage('Discount code must be between 1 and 50 characters'),
  
  // Validate discount_type
  body('discount_type')
    .notEmpty()
    .withMessage('Discount type is required')
    .isIn(['PERCENTAGE', 'FIXED_AMOUNT', 'BUY_X_GET_Y'])
    .withMessage('Discount type must be one of: PERCENTAGE, FIXED_AMOUNT, BUY_X_GET_Y'),
  
  // Validate discount_value
  body('discount_value')
    .notEmpty()
    .withMessage('Discount value is required')
    .isFloat({ min: 0 })
    .withMessage('Discount value must be a positive number')
    .toFloat(), // Convert to float
  
  // Validate min_purchase_amount (optional)
  body('min_purchase_amount')
    .optional()
    .isFloat({ min: 0 })
    .withMessage('Minimum purchase amount must be a positive number')
    .toFloat(), // Convert to float
  
  // Validate max_discount_amount (optional)
  body('max_discount_amount')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as float
      const floatValue = parseFloat(value);
      if (isNaN(floatValue) || floatValue < 0) {
        throw new Error('Maximum discount amount must be a positive number');
      }
      return true;
    })
    .toFloat(), // Convert to float
  
  // Validate effective_from (required for create)
  body('effective_from')
    .notEmpty()
    .withMessage('Effective from date is required')
    .custom((value) => {
      // Accept both ISO 8601 format and date strings (YYYY-MM-DD)
      const date = new Date(value);
      if (isNaN(date.getTime())) {
        throw new Error('Effective from date must be a valid date');
      }
      return true;
    })
    .toDate(), // Convert to date
  
  // Validate effective_to (optional)
  body('effective_to')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation
      if (!value) return true;
      // Accept both ISO 8601 format and date strings (YYYY-MM-DD)
      const date = new Date(value);
      if (isNaN(date.getTime())) {
        throw new Error('Effective to date must be a valid date');
      }
      return true;
    })
    .toDate(), // Convert to date
  
  // Validate max_uses_per_customer (optional)
  body('max_uses_per_customer')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as integer
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Max uses per customer must be a positive integer');
      }
      return true;
    })
    .toInt(), // Convert to integer
  
  // Validate max_total_uses (optional)
  body('max_total_uses')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as integer
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Max total uses must be a positive integer');
      }
      return true;
    })
    .toInt(), // Convert to integer
  
  // Validate active (optional)
  body('active')
    .optional()
    .isBoolean()
    .withMessage('Active must be a boolean value')
    .toBoolean(), // Convert to boolean
  
  // Validate description (optional)
  body('description')
    .optional()
    .trim(),
  
  // Validate rules (optional array)
  body('rules')
    .optional()
    .isArray()
    .withMessage('Rules must be an array'),
  
  body('rules.*.rule_type')
    .optional()
    .isIn(['PRODUCT', 'CATEGORY', 'CUSTOMER', 'ALL'])
    .withMessage('Rule type must be one of: PRODUCT, CATEGORY, CUSTOMER, ALL'),
  
  body('rules.*.product_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  body('rules.*.category_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Category ID must be a positive integer')
    .toInt(), // Convert to integer
  
  body('rules.*.customer_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Customer ID must be a positive integer')
    .toInt(), // Convert to integer
  
  body('rules.*.min_quantity')
    .optional()
    .isFloat({ min: 0 })
    .withMessage('Minimum quantity must be a positive number')
    .toFloat(), // Convert to float
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for updating discount
 */
const validateUpdateDiscount = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('Discount ID is required')
    .isInt({ min: 1 })
    .withMessage('Discount ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate name (optional)
  body('name')
    .optional()
    .trim()
    .isLength({ min: 1, max: 150 })
    .withMessage('Discount name must be between 1 and 150 characters'),
  
  // Validate code (optional)
  body('code')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation
      if (!value || (typeof value === 'string' && value.trim().length === 0)) {
        return true;
      }
      // Validate length if value exists
      const trimmed = String(value).trim();
      if (trimmed.length < 1 || trimmed.length > 50) {
        throw new Error('Discount code must be between 1 and 50 characters');
      }
      return true;
    }),
  
  // Validate discount_type (optional)
  body('discount_type')
    .optional()
    .isIn(['PERCENTAGE', 'FIXED_AMOUNT', 'BUY_X_GET_Y'])
    .withMessage('Discount type must be one of: PERCENTAGE, FIXED_AMOUNT, BUY_X_GET_Y'),
  
  // Validate discount_value (optional)
  body('discount_value')
    .optional()
    .isFloat({ min: 0 })
    .withMessage('Discount value must be a positive number')
    .toFloat(), // Convert to float
  
  // Validate min_purchase_amount (optional)
  body('min_purchase_amount')
    .optional()
    .isFloat({ min: 0 })
    .withMessage('Minimum purchase amount must be a positive number')
    .toFloat(), // Convert to float
  
  // Validate max_discount_amount (optional)
  body('max_discount_amount')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as float
      const floatValue = parseFloat(value);
      if (isNaN(floatValue) || floatValue < 0) {
        throw new Error('Maximum discount amount must be a positive number');
      }
      return true;
    })
    .toFloat(), // Convert to float
  
  // Validate effective_from (optional for update)
  body('effective_from')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If empty/null, skip validation
      if (!value) return true;
      // Accept both ISO 8601 format and date strings (YYYY-MM-DD)
      const date = new Date(value);
      if (isNaN(date.getTime())) {
        throw new Error('Effective from date must be a valid date');
      }
      return true;
    })
    .toDate(), // Convert to date
  
  // Validate effective_to (optional)
  body('effective_to')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation
      if (!value) return true;
      // Accept both ISO 8601 format and date strings (YYYY-MM-DD)
      const date = new Date(value);
      if (isNaN(date.getTime())) {
        throw new Error('Effective to date must be a valid date');
      }
      return true;
    })
    .toDate(), // Convert to date
  
  // Validate max_uses_per_customer (optional)
  body('max_uses_per_customer')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as integer
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Max uses per customer must be a positive integer');
      }
      return true;
    })
    .toInt(), // Convert to integer
  
  // Validate max_total_uses (optional)
  body('max_total_uses')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as integer
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Max total uses must be a positive integer');
      }
      return true;
    })
    .toInt(), // Convert to integer
  
  // Validate active (optional)
  body('active')
    .optional()
    .isBoolean()
    .withMessage('Active must be a boolean value')
    .toBoolean(), // Convert to boolean
  
  // Validate description (optional)
  body('description')
    .optional()
    .trim(),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for getting discount
 */
const validateGetDiscount = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('Discount ID is required')
    .isInt({ min: 1 })
    .withMessage('Discount ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for getting discount by code
 */
const validateGetDiscountByCode = [
  // Validate code parameter
  param('code')
    .notEmpty()
    .withMessage('Discount code is required')
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage('Discount code must be between 1 and 50 characters'),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validate get discounts for product
 * Validates product ID parameter
 */
const validateGetDiscountsForProduct = [
  // Validate productId parameter
  param('productId')
    .notEmpty()
    .withMessage('Product ID is required')
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for deleting discount
 */
const validateDeleteDiscount = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('Discount ID is required')
    .isInt({ min: 1 })
    .withMessage('Discount ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for listing discounts
 */
const validateListDiscounts = [
  // Validate active query parameter (optional)
  query('active')
    .optional()
    .isIn(['true', 'false'])
    .withMessage('Active must be "true" or "false"'),
  
  // Validate discount_type query parameter (optional)
  query('discount_type')
    .optional()
    .isIn(['PERCENTAGE', 'FIXED_AMOUNT', 'BUY_X_GET_Y'])
    .withMessage('Discount type must be one of: PERCENTAGE, FIXED_AMOUNT, BUY_X_GET_Y'),
  
  // Validate search query parameter (optional)
  query('search')
    .optional()
    .trim(),
  
  // Validate page query parameter (optional)
  query('page')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Page must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate limit query parameter (optional)
  query('limit')
    .optional()
    .isInt({ min: 1, max: 100 })
    .withMessage('Limit must be between 1 and 100')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for validating discount
 */
const validateValidateDiscount = [
  // Validate discount_code or discount_id (at least one required)
  body('discount_code')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation (will be checked in the custom validator below)
      if (!value || (typeof value === 'string' && value.trim().length === 0)) {
        return true;
      }
      // Validate length if value exists
      const trimmed = String(value).trim();
      if (trimmed.length < 1 || trimmed.length > 50) {
        throw new Error('Discount code must be between 1 and 50 characters');
      }
      return true;
    }),
  
  body('discount_id')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation (will be checked in the custom validator below)
      if (!value || value === null || value === undefined || value === '') {
        return true;
      }
      // Validate as integer if value exists
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Discount ID must be a positive integer');
      }
      return true;
    }),
  
  body().custom((value) => {
    // At least one of discount_code or discount_id must be provided and non-empty
    const hasCode = value.discount_code && 
                    typeof value.discount_code === 'string' && 
                    value.discount_code.trim().length > 0;
    const hasId = value.discount_id && 
                  value.discount_id !== null && 
                  value.discount_id !== undefined && 
                  value.discount_id !== '' &&
                  !isNaN(parseInt(value.discount_id));
    
    if (!hasCode && !hasId) {
      throw new Error('Either discount_code or discount_id is required'); // Throw error if neither provided
    }
    return true; // Return true if valid
  }),
  
  // Validate customer_id (optional - can be null)
  body('customer_id')
    .optional({ values: 'null' }) // Allow null values
    .custom((value) => {
      // If value is null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as integer
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Customer ID must be a positive integer');
      }
      return true;
    })
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for applying discount
 */
const validateApplyDiscount = [
  // Validate discount_code or discount_id (at least one required)
  body('discount_code')
    .optional()
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage('Discount code must be between 1 and 50 characters'),
  
  body('discount_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Discount ID must be a positive integer')
    .toInt(), // Convert to integer
  
  body().custom((value) => {
    // At least one of discount_code or discount_id must be provided
    if (!value.discount_code && !value.discount_id) {
      throw new Error('Either discount_code or discount_id is required'); // Throw error if neither provided
    }
    return true; // Return true if valid
  }),
  
  // Validate sale_items (required array)
  body('sale_items')
    .notEmpty()
    .withMessage('Sale items are required')
    .isArray({ min: 1 })
    .withMessage('Sale items must be a non-empty array'),
  
  body('sale_items.*.product_id')
    .notEmpty()
    .withMessage('Product ID is required for each sale item')
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // variant_id validation removed - variants not used
  
  body('sale_items.*.quantity')
    .notEmpty()
    .withMessage('Quantity is required for each sale item')
    .isFloat({ min: 0.01 })
    .withMessage('Quantity must be a positive number')
    .toFloat(), // Convert to float
  
  body('sale_items.*.unit_price')
    .notEmpty()
    .withMessage('Unit price is required for each sale item')
    .isFloat({ min: 0 })
    .withMessage('Unit price must be a positive number')
    .toFloat(), // Convert to float
  
  // Validate customer_id (optional - can be null)
  body('customer_id')
    .optional({ values: 'null' }) // Allow null values
    .custom((value) => {
      // If value is null, undefined, or empty string, allow it
      if (value === null || value === undefined || value === '') {
        return true;
      }
      // Otherwise, validate as integer
      const intValue = parseInt(value);
      if (isNaN(intValue) || intValue < 1) {
        throw new Error('Customer ID must be a positive integer');
      }
      return true;
    })
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

// ============================================
// Discount Rule Validations
// ============================================

/**
 * Validation rules for creating discount rule
 */
const validateCreateDiscountRule = [
  // Validate discountId parameter
  param('discountId')
    .notEmpty()
    .withMessage('Discount ID is required')
    .isInt({ min: 1 })
    .withMessage('Discount ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate rule_type
  body('rule_type')
    .notEmpty()
    .withMessage('Rule type is required')
    .isIn(['PRODUCT', 'CATEGORY', 'CUSTOMER', 'ALL'])
    .withMessage('Rule type must be one of: PRODUCT, CATEGORY, CUSTOMER, ALL'),
  
  // Validate product_id (required if rule_type is PRODUCT)
  body('product_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate category_id (required if rule_type is CATEGORY)
  body('category_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Category ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate customer_id (required if rule_type is CUSTOMER)
  body('customer_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Customer ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate min_quantity (optional)
  body('min_quantity')
    .optional()
    .isFloat({ min: 0 })
    .withMessage('Minimum quantity must be a positive number')
    .toFloat(), // Convert to float
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for updating discount rule
 */
const validateUpdateDiscountRule = [
  // Validate ruleId parameter
  param('ruleId')
    .notEmpty()
    .withMessage('Rule ID is required')
    .isInt({ min: 1 })
    .withMessage('Rule ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate rule_type (optional)
  body('rule_type')
    .optional()
    .isIn(['PRODUCT', 'CATEGORY', 'CUSTOMER', 'ALL'])
    .withMessage('Rule type must be one of: PRODUCT, CATEGORY, CUSTOMER, ALL'),
  
  // Validate product_id (optional)
  body('product_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate category_id (optional)
  body('category_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Category ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate customer_id (optional)
  body('customer_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Customer ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate min_quantity (optional)
  body('min_quantity')
    .optional()
    .isFloat({ min: 0 })
    .withMessage('Minimum quantity must be a positive number')
    .toFloat(), // Convert to float
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for getting discount rule
 */
const validateGetDiscountRule = [
  // Validate ruleId parameter
  param('ruleId')
    .notEmpty()
    .withMessage('Rule ID is required')
    .isInt({ min: 1 })
    .withMessage('Rule ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for deleting discount rule
 */
const validateDeleteDiscountRule = [
  // Validate ruleId parameter
  param('ruleId')
    .notEmpty()
    .withMessage('Rule ID is required')
    .isInt({ min: 1 })
    .withMessage('Rule ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for listing discount rules
 */
const validateListDiscountRules = [
  // Validate discountId parameter
  param('discountId')
    .notEmpty()
    .withMessage('Discount ID is required')
    .isInt({ min: 1 })
    .withMessage('Discount ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Run validation
  validate, // Validate request
];

// Export all validation rules
module.exports = {
  // Discount validations
  validateCreateDiscount,
  validateUpdateDiscount,
  validateGetDiscount,
  validateGetDiscountByCode,
  validateGetDiscountsForProduct,
  validateDeleteDiscount,
  validateListDiscounts,
  validateValidateDiscount,
  validateApplyDiscount,
  // Discount rule validations
  validateCreateDiscountRule,
  validateUpdateDiscountRule,
  validateGetDiscountRule,
  validateDeleteDiscountRule,
  validateListDiscountRules,
};

