/**
 * Procurement Validations
 * Validation rules for procurement 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
};

// ============================================
// Vendor Validations
// ============================================

/**
 * Validation rules for creating vendor
 */
const validateCreateVendor = [
  // Validate name
  body('name')
    .notEmpty()
    .withMessage('Vendor name is required')
    .trim()
    .isLength({ min: 1, max: 150 })
    .withMessage('Vendor name must be between 1 and 150 characters'),
  
  // Validate type
  body('type')
    .optional()
    .isIn(['OPEN_MARKET', 'REGISTERED'])
    .withMessage('Vendor type must be OPEN_MARKET or REGISTERED'),
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for updating vendor
 */
const validateUpdateVendor = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('Vendor ID is required')
    .isInt({ min: 1 })
    .withMessage('Vendor ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate name (optional)
  body('name')
    .optional()
    .trim()
    .isLength({ min: 1, max: 150 })
    .withMessage('Vendor name must be between 1 and 150 characters'),
  
  // Validate type (optional)
  body('type')
    .optional()
    .isIn(['OPEN_MARKET', 'REGISTERED'])
    .withMessage('Vendor type must be OPEN_MARKET or REGISTERED'),
  
  // Run validation
  validate, // Validate request
];

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

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

/**
 * Validation rules for listing vendors
 */
const validateListVendors = [
  // Validate type query parameter (optional)
  query('type')
    .optional()
    .isIn(['OPEN_MARKET', 'REGISTERED'])
    .withMessage('Vendor type must be OPEN_MARKET or REGISTERED'),
  
  // 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
];

// ============================================
// Purchase Order Validations
// ============================================

/**
 * Validation rules for creating purchase order
 */
const validateCreatePurchaseOrder = [
  // Validate vendor_id
  body('vendor_id')
    .notEmpty()
    .withMessage('Vendor ID is required')
    .isInt({ min: 1 })
    .withMessage('Vendor ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate status (optional)
  body('status')
    .optional()
    .isIn(['DRAFT', 'CONFIRMED', 'CLOSED'])
    .withMessage('Status must be DRAFT, CONFIRMED, or CLOSED'),
  
  // Validate items (optional array)
  body('items')
    .optional()
    .isArray({ min: 1 })
    .withMessage('Items must be a non-empty array'),
  
  body('items.*.product_id')
    .optional()
    .notEmpty()
    .withMessage('Product ID is required for each item')
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // variant_id removed - variants not used
  
  body('items.*.quantity')
    .optional()
    .notEmpty()
    .withMessage('Quantity is required for each item')
    .isFloat({ min: 0.001 })
    .withMessage('Quantity must be a positive number')
    .toFloat(), // Convert to float
  
  body('items.*.unit_cost')
    .optional()
    .notEmpty()
    .withMessage('Unit cost is required for each item')
    .isFloat({ min: 0 })
    .withMessage('Unit cost must be a non-negative number')
    .toFloat(), // Convert to float
  
  // Dimension fields for RM products (optional - expected/ordered dimensions)
  // These represent the expected dimensions when ordering
  // Actual dimensions may differ when goods are received via GRN
  body('items.*.piece_length')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseFloat(value);
        if (isNaN(num) || num < 0.001) {
          throw new Error('Piece length must be a positive number');
        }
      }
      return true;
    })
    .toFloat(), // Convert to float if valid
  
  body('items.*.piece_width')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseFloat(value);
        if (isNaN(num) || num < 0.001) {
          throw new Error('Piece width must be a positive number');
        }
      }
      return true;
    })
    .toFloat(), // Convert to float if valid
  
  body('items.*.dimension_unit')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '') {
        if (!['inch', 'cm', 'm'].includes(value)) {
          throw new Error('Dimension unit must be one of: inch, cm, m');
        }
      }
      return true;
    }),
  
  body('items.*.pieces_count')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseInt(value);
        if (isNaN(num) || num < 1) {
          throw new Error('Pieces count must be a positive integer');
        }
      }
      return true;
    })
    .toInt(), // Convert to integer if valid
  
  // Run validation
  validate, // Validate request
];

/**
 * Validation rules for updating purchase order
 */
const validateUpdatePurchaseOrder = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('Purchase order ID is required')
    .isInt({ min: 1 })
    .withMessage('Purchase order ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate vendor_id (optional)
  body('vendor_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Vendor ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate status (optional)
  body('status')
    .optional()
    .isIn(['DRAFT', 'CONFIRMED', 'CLOSED'])
    .withMessage('Status must be DRAFT, CONFIRMED, or CLOSED'),
  
  // Validate items (optional array)
  body('items')
    .optional()
    .isArray()
    .withMessage('Items must be an array'),
  
  body('items.*.product_id')
    .optional()
    .notEmpty()
    .withMessage('Product ID is required for each item')
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  body('items.*.quantity')
    .optional()
    .notEmpty()
    .withMessage('Quantity is required for each item')
    .isFloat({ min: 0.001 })
    .withMessage('Quantity must be a positive number')
    .toFloat(), // Convert to float
  
  body('items.*.unit_cost')
    .optional()
    .notEmpty()
    .withMessage('Unit cost is required for each item')
    .isFloat({ min: 0 })
    .withMessage('Unit cost must be a non-negative number')
    .toFloat(), // Convert to float
  
  // Dimension fields for RM products (optional - expected/ordered dimensions)
  body('items.*.piece_length')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseFloat(value);
        if (isNaN(num) || num < 0.001) {
          throw new Error('Piece length must be a positive number');
        }
      }
      return true;
    })
    .toFloat(), // Convert to float if valid
  
  body('items.*.piece_width')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseFloat(value);
        if (isNaN(num) || num < 0.001) {
          throw new Error('Piece width must be a positive number');
        }
      }
      return true;
    })
    .toFloat(), // Convert to float if valid
  
  body('items.*.dimension_unit')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '') {
        if (!['inch', 'cm', 'm'].includes(value)) {
          throw new Error('Dimension unit must be one of: inch, cm, m');
        }
      }
      return true;
    }),
  
  body('items.*.pieces_count')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseInt(value);
        if (isNaN(num) || num < 1) {
          throw new Error('Pieces count must be a positive integer');
        }
      }
      return true;
    })
    .toInt(), // Convert to integer if valid
  
  // Run validation
  validate, // Validate request
];

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

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

/**
 * Validation rules for listing purchase orders
 */
const validateListPurchaseOrders = [
  // Validate vendor_id query parameter (optional)
  query('vendor_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Vendor ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate status query parameter (optional)
  query('status')
    .optional()
    .isIn(['DRAFT', 'CONFIRMED', 'CLOSED'])
    .withMessage('Status must be DRAFT, CONFIRMED, or CLOSED'),
  
  // 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 updating purchase order status
 */
const validateUpdatePurchaseOrderStatus = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('Purchase order ID is required')
    .isInt({ min: 1 })
    .withMessage('Purchase order ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate status
  body('status')
    .notEmpty()
    .withMessage('Status is required')
    .isIn(['DRAFT', 'CONFIRMED', 'CLOSED'])
    .withMessage('Status must be DRAFT, CONFIRMED, or CLOSED'),
  
  // Run validation
  validate, // Validate request
];

// ============================================
// GRN Validations
// ============================================

/**
 * Validation rules for creating GRN
 */
const validateCreateGRN = [
  // Validate purchase_order_id (optional)
  body('purchase_order_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Purchase order ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate vendor_id (optional)
  body('vendor_id')
    .optional()
    .isInt({ min: 1 })
    .withMessage('Vendor ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate received_at (optional)
  body('received_at')
    .optional()
    .isISO8601()
    .withMessage('Received date must be a valid ISO 8601 date')
    .toDate(), // Convert to date
  
  // Validate items (required array)
  body('items')
    .notEmpty()
    .withMessage('Items are required')
    .isArray({ min: 1 })
    .withMessage('Items must be a non-empty array'),
  
  body('items.*.product_id')
    .notEmpty()
    .withMessage('Product ID is required for each item')
    .isInt({ min: 1 })
    .withMessage('Product ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // variant_id removed - variants not used
  
  body('items.*.quantity')
    .notEmpty()
    .withMessage('Quantity is required for each item')
    .isFloat({ min: 0.001 })
    .withMessage('Quantity must be a positive number')
    .toFloat(), // Convert to float
  
  body('items.*.unit_cost')
    .notEmpty()
    .withMessage('Unit cost is required for each item')
    .isFloat({ min: 0 })
    .withMessage('Unit cost must be a non-negative number')
    .toFloat(), // Convert to float
  
  // Dimension fields for RM products (optional - only required for dimension-based RM)
  // Allow empty/null/0 values - they'll be validated in the service layer based on product type
  // Using checkFalsy: true skips validation for 0, '', null, undefined
  body('items.*.piece_length')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseFloat(value);
        if (isNaN(num) || num < 0.001) {
          throw new Error('Piece length must be a positive number');
        }
      }
      return true;
    })
    .toFloat(), // Convert to float if valid
  
  body('items.*.piece_width')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseFloat(value);
        if (isNaN(num) || num < 0.001) {
          throw new Error('Piece width must be a positive number');
        }
      }
      return true;
    })
    .toFloat(), // Convert to float if valid
  
  body('items.*.dimension_unit')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '') {
        if (!['inch', 'cm', 'm'].includes(value)) {
          throw new Error('Dimension unit must be one of: inch, cm, m');
        }
      }
      return true;
    }),
  
  body('items.*.pieces_count')
    .optional({ checkFalsy: true })
    .custom((value) => {
      // If value is provided and not falsy, validate it
      if (value !== null && value !== undefined && value !== '' && value !== 0) {
        const num = parseInt(value);
        if (isNaN(num) || num < 1) {
          throw new Error('Pieces count must be a positive integer');
        }
      }
      return true;
    })
    .toInt(), // Convert to integer if valid
  
  // Run validation
  validate, // Validate request
];

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

/**
 * Validation rules for listing GRNs
 */
const validateListGRNs = [
  // Validate purchase_order_id query parameter (optional)
  query('purchase_order_id')
    .optional()
    .custom((value) => {
      // Allow null string or integer
      if (value === 'null' || value === null) {
        return true; // Allow null
      }
      return /^\d+$/.test(value); // Validate integer
    })
    .withMessage('Purchase order ID must be a positive integer or null'),
  
  // Validate vendor_id query parameter (optional)
  query('vendor_id')
    .optional()
    .custom((value) => {
      // Allow null string or integer
      if (value === 'null' || value === null) {
        return true; // Allow null
      }
      return /^\d+$/.test(value); // Validate integer
    })
    .withMessage('Vendor ID must be a positive integer or null'),
  
  // Validate po_number query parameter (optional)
  query('po_number')
    .optional()
    .isString()
    .withMessage('PO number must be a string')
    .trim(),
  
  // Validate status query parameter (optional)
  query('status')
    .optional()
    .isIn(['processed', 'pending'])
    .withMessage('Status must be either "processed" or "pending"'),
  
  // Validate start_date query parameter (optional)
  query('start_date')
    .optional()
    .isISO8601()
    .withMessage('Start date must be a valid ISO 8601 date'),
  
  // Validate end_date query parameter (optional)
  query('end_date')
    .optional()
    .isISO8601()
    .withMessage('End date must be a valid ISO 8601 date'),
  
  // 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 processing GRN
 */
const validateProcessGRN = [
  // Validate id parameter
  param('id')
    .notEmpty()
    .withMessage('GRN ID is required')
    .isInt({ min: 1 })
    .withMessage('GRN ID must be a positive integer')
    .toInt(), // Convert to integer
  
  // Validate generateUIDs (optional)
  body('generateUIDs')
    .optional()
    .isBoolean()
    .withMessage('generateUIDs must be a boolean value')
    .toBoolean(), // Convert to boolean
  
  // Run validation
  validate, // Validate request
];

// Export all validation rules
module.exports = {
  // Vendor validations
  validateCreateVendor,
  validateUpdateVendor,
  validateGetVendor,
  validateDeleteVendor,
  validateListVendors,
  // Purchase order validations
  validateCreatePurchaseOrder,
  validateUpdatePurchaseOrder,
  validateGetPurchaseOrder,
  validateDeletePurchaseOrder,
  validateListPurchaseOrders,
  validateUpdatePurchaseOrderStatus,
  // GRN validations
  validateCreateGRN,
  validateGetGRN,
  validateListGRNs,
  validateProcessGRN,
};
