// src/middleware/validation.js
// === SECURITY HARDENING: INPUT VALIDATION ===
// Request validation middleware using simple validation

/**
 * Validation middleware for request bodies
 * Uses simple validation without external dependencies
 */

/**
 * Validate phone number format
 */
function validatePhone(phone) {
  if (!phone || typeof phone !== 'string') {
    return { valid: false, error: 'phone_number must be a string' };
  }
  const trimmed = phone.trim();
  if (trimmed.length < 10 || trimmed.length > 20) {
    return { valid: false, error: 'phone_number must be 10-20 characters' };
  }
  return { valid: true };
}

/**
 * Validate OTP code format
 */
function validateOtpCode(code) {
  if (!code) {
    return { valid: false, error: 'code is required' };
  }
  
  // Convert to string if it's a number (JSON may send numbers)
  const codeString = String(code);
  
  // Validate it's exactly 6 digits
  if (!/^\d{6}$/.test(codeString)) {
    return { valid: false, error: 'code must be exactly 6 digits' };
  }
  
  return { valid: true, normalizedCode: codeString };
}

/**
 * Validate device ID
 */
function validateDeviceId(deviceId) {
  if (deviceId === undefined || deviceId === null) {
    return { valid: true }; // Optional field
  }
  if (typeof deviceId !== 'string') {
    return { valid: false, error: 'device_id must be a string' };
  }
  if (deviceId.length > 128) {
    return { valid: false, error: 'device_id must be 128 characters or less' };
  }
  return { valid: true };
}

/**
 * Validate and sanitize device info object
 * Truncates long values instead of rejecting them (safe for informational fields)
 */
function validateDeviceInfo(deviceInfo) {
  if (deviceInfo === undefined || deviceInfo === null) {
    return { valid: true }; // Optional field
  }
  if (typeof deviceInfo !== 'object' || Array.isArray(deviceInfo)) {
    return { valid: false, error: 'device_info must be an object' };
  }

  const maxLength = 100;
  const fields = ['platform', 'model', 'os_version', 'app_version', 'language_code', 'timezone'];
  
  for (const field of fields) {
    if (deviceInfo[field] !== undefined && deviceInfo[field] !== null) {
      if (typeof deviceInfo[field] !== 'string') {
        return { valid: false, error: `device_info.${field} must be a string` };
      }
      // Truncate long values instead of rejecting (safe for informational fields)
      if (deviceInfo[field].length > maxLength) {
        deviceInfo[field] = deviceInfo[field].substring(0, maxLength);
      }
    }
  }

  return { valid: true };
}

/**
 * Validate refresh token
 */
function validateRefreshToken(refreshToken) {
  if (!refreshToken || typeof refreshToken !== 'string') {
    return { valid: false, error: 'refresh_token must be a string' };
  }
  if (refreshToken.length > 1000) {
    return { valid: false, error: 'refresh_token is too long' };
  }
  return { valid: true };
}

/**
 * Validate request body size (prevent DoS)
 */
function validateBodySize(body, maxSize = 10000) {
  const bodyStr = JSON.stringify(body);
  if (bodyStr.length > maxSize) {
    return { valid: false, error: 'Request body too large' };
  }
  return { valid: true };
}

/**
 * Middleware: Validate /auth/request-otp request
 */
function validateRequestOtpBody(req, res, next) {
  const { phone_number } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 1000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  // Validate phone
  if (!phone_number) {
    return res.status(400).json({ error: 'phone_number is required' });
  }

  const phoneCheck = validatePhone(phone_number);
  if (!phoneCheck.valid) {
    return res.status(400).json({ error: phoneCheck.error });
  }

  next();
}

/**
 * Middleware: Validate /auth/verify-otp request
 */
function validateVerifyOtpBody(req, res, next) {
  const { phone_number, code, device_id, device_info } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 2000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  // Validate required fields
  if (!phone_number) {
    return res.status(400).json({ error: 'phone_number is required' });
  }

  if (!code) {
    return res.status(400).json({ error: 'code is required' });
  }

  // Validate each field
  const phoneCheck = validatePhone(phone_number);
  if (!phoneCheck.valid) {
    return res.status(400).json({ error: phoneCheck.error });
  }

  const codeCheck = validateOtpCode(code);
  if (!codeCheck.valid) {
    return res.status(400).json({ error: codeCheck.error });
  }
  
  // Normalize code to string (in case it came as number from JSON)
  // This ensures bcrypt.compare works correctly
  if (codeCheck.normalizedCode) {
    req.body.code = codeCheck.normalizedCode;
  } else {
    req.body.code = String(code);
  }

  const deviceIdCheck = validateDeviceId(device_id);
  if (!deviceIdCheck.valid) {
    return res.status(400).json({ error: deviceIdCheck.error });
  }

  const deviceInfoCheck = validateDeviceInfo(device_info);
  if (!deviceInfoCheck.valid) {
    return res.status(400).json({ error: deviceInfoCheck.error });
  }

  next();
}

/**
 * Middleware: Validate /auth/refresh request
 */
function validateRefreshTokenBody(req, res, next) {
  const { refresh_token } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 2000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  if (!refresh_token) {
    return res.status(400).json({ error: 'refresh_token is required' });
  }

  const tokenCheck = validateRefreshToken(refresh_token);
  if (!tokenCheck.valid) {
    return res.status(400).json({ error: tokenCheck.error });
  }

  next();
}

/**
 * Middleware: Validate /auth/logout request
 */
function validateLogoutBody(req, res, next) {
  const { refresh_token } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 2000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  if (!refresh_token) {
    return res.status(400).json({ error: 'refresh_token is required' });
  }

  const tokenCheck = validateRefreshToken(refresh_token);
  if (!tokenCheck.valid) {
    return res.status(400).json({ error: tokenCheck.error });
  }

  next();
}

// === VALIDATION: USER ROUTES ===

/**
 * Validate marketplace role (for active_role field)
 * Maps old values to new schema:
 * - 'seller' or 'buyer' -> 'seller_buyer'
 * - 'service_provider' -> 'service_provider'
 * 
 * Database: active_role uses listing_role_enum: ('seller_buyer', 'service_provider')
 */
function validateMarketplaceRole(role) {
  if (role === undefined || role === null) {
    return { valid: true }; // Optional field
  }
  if (typeof role !== 'string') {
    return { valid: false, error: 'user_type must be a string' };
  }
  // Normalize to lowercase for comparison
  const normalized = role.toLowerCase();
  
  // Map old values to new schema
  const roleMapping = {
    'seller': 'seller_buyer',
    'buyer': 'seller_buyer',
    'service_provider': 'service_provider'
  };
  
  if (roleMapping[normalized]) {
    return { valid: true, mappedValue: roleMapping[normalized] };
  }
  
  // Also accept new values directly
  const allowedValues = ['seller_buyer', 'service_provider'];
  if (allowedValues.includes(normalized)) {
    return { valid: true, mappedValue: normalized };
  }
  
  return { valid: false, error: `user_type must be one of: seller, buyer, service_provider, seller_buyer` };
}

/**
 * Validate name string (optional, trimmed, max 100)
 */
function validateName(name) {
  if (name === undefined || name === null) {
    return { valid: true }; // Optional field
  }
  if (typeof name !== 'string') {
    return { valid: false, error: 'name must be a string' };
  }
  const trimmed = name.trim();
  if (trimmed.length > 100) {
    return { valid: false, error: 'name must be 100 characters or less' };
  }
  return { valid: true };
}

/**
 * Middleware: Validate PUT /users/me request body
 * Validates: name (required string, trimmed, max 100), user_type (marketplace role: seller/buyer/service_provider)
 * Maps user_type to active_role field in database
 * Rejects: unknown extra fields, invalid types, overly long strings
 */
function validateUpdateProfileBody(req, res, next) {
  const { name, user_type } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 1000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  // Check for unknown fields (only allow 'name' and 'user_type')
  const allowedFields = ['name', 'user_type'];
  const bodyKeys = Object.keys(req.body);
  const unknownFields = bodyKeys.filter(key => !allowedFields.includes(key));
  if (unknownFields.length > 0) {
    return res.status(400).json({ 
      error: `Unknown fields not allowed: ${unknownFields.join(', ')}` 
    });
  }

  // Validate name (required)
  if (!name) {
    return res.status(400).json({ error: 'name is required' });
  }
  const nameCheck = validateName(name);
  if (!nameCheck.valid) {
    return res.status(400).json({ error: nameCheck.error });
  }

  // Validate user_type (required) - maps to active_role in database
  if (!user_type) {
    return res.status(400).json({ error: 'user_type is required' });
  }
  const roleCheck = validateMarketplaceRole(user_type);
  if (!roleCheck.valid) {
    return res.status(400).json({ error: roleCheck.error });
  }

  // Trim name and store mapped role value
  if (name && typeof name === 'string') {
    req.body.name = name.trim();
  }
  // Store the mapped marketplace role value for the route handler
  req.body.active_role = roleCheck.mappedValue;

  next();
}

/**
 * Middleware: Validate DELETE /users/me/devices/:device_id param
 * Validates: device_id as string with max length 100
 * Note: device_identifier in database is TEXT, not UUID, so we validate as string
 */
function validateDeviceIdParam(req, res, next) {
  const { device_id } = req.params;

  if (!device_id) {
    return res.status(400).json({ error: 'device_id is required' });
  }

  if (typeof device_id !== 'string') {
    return res.status(400).json({ error: 'device_id must be a string' });
  }

  if (device_id.length > 100) {
    return res.status(400).json({ error: 'device_id must be 100 characters or less' });
  }

  if (device_id.length === 0) {
    return res.status(400).json({ error: 'device_id cannot be empty' });
  }

  next();
}

/**
 * Middleware: Validate POST /users/me/logout-all-other-devices request body
 * Validates: current_device_id (optional, same validation as device_id)
 * Rejects: unknown extra fields
 */
function validateLogoutOthersBody(req, res, next) {
  const { current_device_id } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 1000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  // Check for unknown fields (only allow 'current_device_id')
  const allowedFields = ['current_device_id'];
  const bodyKeys = Object.keys(req.body);
  const unknownFields = bodyKeys.filter(key => !allowedFields.includes(key));
  if (unknownFields.length > 0) {
    return res.status(400).json({ 
      error: `Unknown fields not allowed: ${unknownFields.join(', ')}` 
    });
  }

  // Validate current_device_id (optional, but if present must be valid)
  if (current_device_id !== undefined && current_device_id !== null) {
    if (typeof current_device_id !== 'string') {
      return res.status(400).json({ error: 'current_device_id must be a string' });
    }
    if (current_device_id.length > 100) {
      return res.status(400).json({ error: 'current_device_id must be 100 characters or less' });
    }
    if (current_device_id.length === 0) {
      return res.status(400).json({ error: 'current_device_id cannot be empty' });
    }
  }

  next();
}

/**
 * Validate signup request body
 */
function validateSignupBody(req, res, next) {
  const { name, phone_number, state, district, city_village, device_id, device_info } = req.body;

  // Check body size
  const sizeCheck = validateBodySize(req.body, 2000);
  if (!sizeCheck.valid) {
    return res.status(400).json({ error: sizeCheck.error });
  }

  // Validate required fields
  if (!name) {
    return res.status(400).json({ error: 'name is required' });
  }

  if (!phone_number) {
    return res.status(400).json({ error: 'phone_number is required' });
  }

  // Validate each field
  const nameCheck = validateName(name);
  if (!nameCheck.valid) {
    return res.status(400).json({ error: nameCheck.error });
  }

  const phoneCheck = validatePhone(phone_number);
  if (!phoneCheck.valid) {
    return res.status(400).json({ error: phoneCheck.error });
  }

  // Validate optional location fields
  if (state !== undefined && state !== null) {
    if (typeof state !== 'string' || state.trim().length > 100) {
      return res.status(400).json({ error: 'state must be a string with max 100 characters' });
    }
  }

  if (district !== undefined && district !== null) {
    if (typeof district !== 'string' || district.trim().length > 100) {
      return res.status(400).json({ error: 'district must be a string with max 100 characters' });
    }
  }

  if (city_village !== undefined && city_village !== null) {
    if (typeof city_village !== 'string' || city_village.trim().length > 150) {
      return res.status(400).json({ error: 'city_village must be a string with max 150 characters' });
    }
  }

  const deviceIdCheck = validateDeviceId(device_id);
  if (!deviceIdCheck.valid) {
    return res.status(400).json({ error: deviceIdCheck.error });
  }

  const deviceInfoCheck = validateDeviceInfo(device_info);
  if (!deviceInfoCheck.valid) {
    return res.status(400).json({ error: deviceInfoCheck.error });
  }

  // Trim and sanitize
  if (name && typeof name === 'string') {
    req.body.name = name.trim();
  }
  if (state && typeof state === 'string') {
    req.body.state = state.trim();
  }
  if (district && typeof district === 'string') {
    req.body.district = district.trim();
  }
  if (city_village && typeof city_village === 'string') {
    req.body.city_village = city_village.trim();
  }

  next();
}

module.exports = {
  validateRequestOtpBody,
  validateVerifyOtpBody,
  validateRefreshTokenBody,
  validateLogoutBody,
  validateSignupBody,
  // === VALIDATION: USER ROUTES ===
  validateUpdateProfileBody,
  validateDeviceIdParam,
  validateLogoutOthersBody,
};

