/**
 * Payments Validations
 * Validation rules for payment 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('Payment 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
};

/**
 * Validation rules for creating payment (Cash/Bank)
 */
const validateCreatePayment = [
  // Validate sale_id
  body('sale_id')
    .notEmpty()
    .withMessage('Sale ID is required')
    .isInt({ min: 1 })
    .withMessage('Sale ID must be a positive integer'),
  
  // Validate provider
  body('provider')
    .notEmpty()
    .withMessage('Payment provider is required')
    .isIn(['CASH', 'CARD', 'MPESA'])
    .withMessage('Provider must be one of: CASH, CARD, MPESA'),
  
  // Validate amount
  body('amount')
    .notEmpty()
    .withMessage('Payment amount is required')
    .isFloat({ min: 0.01 })
    .withMessage('Payment amount must be greater than 0')
    .toFloat(), // Convert to float
  
  // Validate reference (optional)
  body('reference')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation
      if (!value || value === null || value === undefined || value === '') {
        return true;
      }
      // Validate length if value exists
      const trimmed = String(value).trim();
      if (trimmed.length > 150) {
        throw new Error('Reference must not exceed 150 characters');
      }
      return true;
    }),
  
  // Validate card_reference (optional, for card payments)
  body('card_reference')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation
      if (!value || value === null || value === undefined || value === '') {
        return true;
      }
      // Validate length if value exists
      const trimmed = String(value).trim();
      if (trimmed.length > 100) {
        throw new Error('Card reference must not exceed 100 characters');
      }
      return true;
    }),
  
  // Validate mpesa_phone_number (optional, for M-Pesa payments)
  body('mpesa_phone_number')
    .optional({ checkFalsy: true }) // Allow null, undefined, empty string
    .custom((value) => {
      // If empty/null, skip validation
      if (!value || value === null || value === undefined || value === '') {
        return true;
      }
      // Validate phone number format
      const trimmed = String(value).trim().replace(/\s+/g, ''); // Remove spaces
      if (trimmed.length > 20) {
        throw new Error('Phone number must not exceed 20 characters');
      }
      // Accept various formats:
      // - E.164: +254712345678 (13 chars with +)
      // - Kenyan format: 0701234567 (10 chars), 712345678 (9 chars), 254712345678 (12 chars)
      // - International without +: 254712345678 (12 chars)
      const e164Regex = /^\+?[1-9]\d{1,14}$/; // E.164 format (starts with 1-9, not 0)
      // Kenyan mobile formats: 07XXXXXXXX (10), 7XXXXXXXX (9), 2547XXXXXXXX (12), +2547XXXXXXXX (13)
      const kenyanFormatRegex = /^(?:\+?254|0)?(7\d{8})$/; // Matches: 07XXXXXXXX, 7XXXXXXXX, 2547XXXXXXXX, +2547XXXXXXXX
      
      // Check if it's E.164 format (but allow numbers starting with 0 for Kenyan format)
      const isE164 = trimmed.startsWith('+') && /^\+254[1-9]\d{8}$/.test(trimmed);
      const isKenyan = kenyanFormatRegex.test(trimmed);
      
      if (isE164 || isKenyan) {
        // Valid format - will be normalized in service layer
        return true;
      } else {
        throw new Error('Invalid phone number format. Use formats like: 0701234567, 712345678, +254712345678, or 254712345678');
      }
    }),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for processing Paystack payment
 */
const validateProcessPaystackPayment = [
  // Validate sale_id
  body('sale_id')
    .notEmpty()
    .withMessage('Sale ID is required')
    .isInt({ min: 1 })
    .withMessage('Sale ID must be a positive integer'),
  
  // Validate email
  body('email')
    .notEmpty()
    .withMessage('Email is required')
    .isEmail()
    .withMessage('Invalid email format')
    .normalizeEmail(), // Normalize email
  
  // Validate amount
  body('amount')
    .notEmpty()
    .withMessage('Payment amount is required')
    .isFloat({ min: 0.01 })
    .withMessage('Payment amount must be greater than 0')
    .toFloat(), // Convert to float
  
  // Validate metadata (optional)
  body('metadata')
    .optional()
    .isObject()
    .withMessage('Metadata must be an object'),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for verifying Paystack payment
 */
const validateVerifyPaystackPayment = [
  // Validate reference parameter
  param('reference')
    .notEmpty()
    .withMessage('Payment reference is required')
    .trim()
    .isLength({ min: 1, max: 150 })
    .withMessage('Reference must be between 1 and 150 characters'),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for processing mobile money payment
 */
const validateProcessMobileMoneyPayment = [
  // Validate sale_id
  body('sale_id')
    .notEmpty()
    .withMessage('Sale ID is required')
    .isInt({ min: 1 })
    .withMessage('Sale ID must be a positive integer'),
  
  // Validate amount
  body('amount')
    .notEmpty()
    .withMessage('Payment amount is required')
    .isFloat({ min: 0.01 })
    .withMessage('Payment amount must be greater than 0')
    .toFloat(), // Convert to float
  
  // Validate phone_number
  body('phone_number')
    .notEmpty()
    .withMessage('Phone number is required')
    .trim()
    .matches(/^\+?[1-9]\d{1,14}$/)
    .withMessage('Invalid phone number format (use E.164 format, e.g., +254712345678)'),
  
  // Run validation
  validate, // Validate request
];

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

/**
 * Validation rules for listing payments
 */
const validateListPayments = [
  // Validate page query parameter
  query('page')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Page must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate limit query parameter
  query('limit')
    .optional()
    .isInt({ min: 1, max: 100 })
    .withMessage('Limit must be between 1 and 100')
    .toInt(), // Convert to integer
  
  // Validate sale_id query parameter
  query('sale_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Sale ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate provider query parameter
  query('provider')
    .optional()
    .isIn(['CASH', 'CARD', 'MPESA'])
    .withMessage('Provider must be one of: CASH, CARD, MPESA'),
  
  // Validate status query parameter
  query('status')
    .optional()
    .isIn(['PENDING', 'SUCCESS', 'FAILED'])
    .withMessage('Status must be one of: PENDING, SUCCESS, FAILED'),
  
  // Validate start_date query parameter
  query('start_date')
    .optional()
    .isISO8601()
    .withMessage('Start date must be a valid ISO 8601 date')
    .toDate(), // Convert to date
  
  // Validate end_date query parameter
  query('end_date')
    .optional()
    .isISO8601()
    .withMessage('End date must be a valid ISO 8601 date')
    .toDate(), // Convert to date
  
  // Run validation
  validate, // Validate request
];

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

/**
 * Validation rules for querying STK Push status
 */
const validateQuerySTKPushStatus = [
  // Validate checkoutRequestID parameter
  param('checkoutRequestID')
    .notEmpty()
    .withMessage('Checkout Request ID is required')
    .trim()
    .isLength({ min: 1, max: 50 })
    .withMessage('Checkout Request ID must be between 1 and 50 characters'),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for manually confirming M-Pesa payment
 */
const validateManuallyConfirmMPesaPayment = [
  // Validate payment_id
  body('payment_id')
    .notEmpty()
    .withMessage('Payment ID is required')
    .isInt({ min: 1 })
    .withMessage('Payment ID must be a positive integer')
    .toInt(), // Convert to integer

  // Validate mpesa_transaction_code
  body('mpesa_transaction_code')
    .notEmpty()
    .withMessage('M-Pesa transaction code is required')
    .trim()
    .isLength({ min: 5, max: 50 })
    .withMessage('M-Pesa transaction code must be between 5 and 50 characters')
    .matches(/^[A-Z0-9]+$/)
    .withMessage('M-Pesa transaction code must contain only uppercase letters and numbers'),

  // Run validation
  validate, // Validate request
];

// Export validation rules
module.exports = {
  validateCreatePayment, // Create payment validation
  validateProcessPaystackPayment, // Process Paystack payment validation
  validateVerifyPaystackPayment, // Verify Paystack payment validation
  validateProcessMobileMoneyPayment, // Process mobile money payment validation
  validateQuerySTKPushStatus, // Query STK Push status validation
  validateManuallyConfirmMPesaPayment, // Manually confirm M-Pesa payment validation
  validateGetPayment, // Get payment validation
  validateListPayments, // List payments validation
  validateGetSalePayments, // Get sale payments validation
};
