/**
 * User Management Service
 * Business logic for user management operations
 */

// Import User model
const { User } = require('../../../models');
// Import custom error classes
const { NotFoundError, ValidationError, ConflictError } = require('../../../utils/errors');
// Import bcrypt for password hashing
const bcrypt = require('bcryptjs');

/**
 * Create a new user
 * @param {Object} userData - User data (username, email, password, full_name, role, pin_code)
 * @returns {Promise<Object>} Created user object
 * @throws {ValidationError} If validation fails
 * @throws {ConflictError} If username or email already exists
 */
const createUser = async (userData) => {
  // Extract user data
  const { username, email, password, full_name, role, pin_code, active = true } = userData;
  
  // Validate required fields
  if (!username || !email || !password) {
    throw new ValidationError('Username, email, and password are required');
  }
  
  // Validate role
  const validRoles = ['cashier', 'manager', 'system_admin'];
  if (role && !validRoles.includes(role)) {
    throw new ValidationError(`Role must be one of: ${validRoles.join(', ')}`);
  }
  
  // Check if username already exists
  const existingUsername = await User.findOne({ where: { username } });
  if (existingUsername) {
    throw new ConflictError('Username already exists');
  }
  
  // Check if email already exists
  const existingEmail = await User.findOne({ where: { email } });
  if (existingEmail) {
    throw new ConflictError('Email already exists');
  }
  
  // Create user (password and PIN will be hashed by model hooks)
  const user = await User.create({
    username, // Username
    email, // Email
    password, // Password (will be hashed)
    full_name, // Full name
    role: role || 'cashier', // Role (default: cashier)
    pin_code, // PIN code (optional, will be hashed if provided)
    active, // Active status
  });
  
  // Return user data (password excluded by toJSON)
  return user.toJSON();
};

/**
 * Get user by ID
 * @param {number} userId - User ID
 * @returns {Promise<Object>} User object
 * @throws {NotFoundError} If user not found
 */
const getUserById = async (userId) => {
  // Find user by ID
  const user = await User.findByPk(userId);
  
  // If user not found, throw not found error
  if (!user) {
    throw new NotFoundError('User not found');
  }
  
  // Return user data (password excluded by toJSON)
  return user.toJSON();
};

/**
 * Update user
 * @param {number} userId - User ID
 * @param {Object} updateData - User data to update
 * @returns {Promise<Object>} Updated user object
 * @throws {NotFoundError} If user not found
 * @throws {ConflictError} If username or email already exists
 */
const updateUser = async (userId, updateData) => {
  // Find user by ID
  const user = await User.findByPk(userId);
  
  // If user not found, throw not found error
  if (!user) {
    throw new NotFoundError('User not found');
  }
  
  // Extract update data
  const { username, email, password, full_name, role, pin_code, active } = updateData;
  
  // Validate role if provided
  if (role) {
    const validRoles = ['cashier', 'manager', 'system_admin'];
    if (!validRoles.includes(role)) {
      throw new ValidationError(`Role must be one of: ${validRoles.join(', ')}`);
    }
  }
  
  // Check username uniqueness if username is being changed
  if (username && username !== user.username) {
    const existingUsername = await User.findOne({ where: { username } });
    if (existingUsername) {
      throw new ConflictError('Username already exists');
    }
  }
  
  // Check email uniqueness if email is being changed
  if (email && email !== user.email) {
    const existingEmail = await User.findOne({ where: { email } });
    if (existingEmail) {
      throw new ConflictError('Email already exists');
    }
  }
  
  // Build update object (only include provided fields)
  const updateFields = {};
  if (username) updateFields.username = username;
  if (email) updateFields.email = email;
  if (password) updateFields.password = password; // Will be hashed by model hook
  if (full_name !== undefined) updateFields.full_name = full_name;
  if (role) updateFields.role = role;
  if (pin_code !== undefined) updateFields.pin_code = pin_code; // Will be hashed by model hook
  if (active !== undefined) updateFields.active = active;
  
  // Update user
  await user.update(updateFields);
  
  // Reload user to get updated data
  await user.reload();
  
  // Return updated user data (password excluded by toJSON)
  return user.toJSON();
};

/**
 * Delete user (soft delete by setting active to false)
 * @param {number} userId - User ID
 * @returns {Promise<Object>} Deleted user object
 * @throws {NotFoundError} If user not found
 */
const deleteUser = async (userId) => {
  // Find user by ID
  const user = await User.findByPk(userId);
  
  // If user not found, throw not found error
  if (!user) {
    throw new NotFoundError('User not found');
  }
  
  // Soft delete: set active to false
  await user.update({ active: false });
  
  // Return deleted user data (password excluded by toJSON)
  return user.toJSON();
};

/**
 * List users with pagination and filters
 * @param {Object} options - Query options (page, limit, role, active)
 * @returns {Promise<Object>} Paginated users list
 */
const listUsers = async (options = {}) => {
  // Extract query options
  const { page = 1, limit = 10, role, active } = options;
  
  // Build where clause for filtering
  const where = {};
  if (role) where.role = role; // Filter by role
  if (active !== undefined) where.active = active === 'true' || active === true; // Filter by active status
  
  // Calculate offset for pagination
  const offset = (page - 1) * limit;
  
  // Find users with pagination
  const { count, rows } = await User.findAndCountAll({
    where, // Filter conditions
    limit: parseInt(limit), // Number of records per page
    offset: parseInt(offset), // Skip records
    order: [['created_at', 'DESC']], // Order by creation date (newest first)
  });
  
  // Calculate pagination metadata
  const totalPages = Math.ceil(count / limit); // Total number of pages
  
  // Return paginated results
  return {
    users: rows.map(user => user.toJSON()), // Convert to JSON (passwords excluded)
    pagination: {
      page: parseInt(page), // Current page
      limit: parseInt(limit), // Records per page
      total: count, // Total number of records
      totalPages, // Total number of pages
    },
  };
};

// Export service functions
module.exports = {
  createUser, // Create user
  getUserById, // Get user by ID
  updateUser, // Update user
  deleteUser, // Delete user (soft delete)
  listUsers, // List users with pagination
};

