/**
 * Customers Service
 * Business logic for customer management
 */

// Import Customer and related models
const { Customer, PriceList, TaxExemption, Sale } = require('../../../models');
// Import custom error classes
const { NotFoundError, ValidationError } = require('../../../utils/errors');
// Import logger for logging
const logger = require('../../../utils/logger');
// Import Sequelize operators
const { Op } = require('sequelize');

/**
 * Create customer
 * Creates a new customer
 * @param {Object} customerData - Customer data (name, customer_type, kra_pin, credit_limit, price_list_id)
 * @returns {Promise<Object>} Created customer
 */
const createCustomer = async (customerData) => {
  // Extract customer data
  const { name, customer_type = 'B2C', email = null, phone = null, kra_pin = null, credit_limit = 0, price_list_id = null } = customerData; // Extract data
  
  // Validate required fields
  if (!name) {
    throw new ValidationError('Customer name is required'); // Throw error if name missing
  }
  
  // Validate customer_type
  const validTypes = ['B2C', 'B2B']; // Valid customer types
  if (!validTypes.includes(customer_type)) {
    throw new ValidationError(`Customer type must be one of: ${validTypes.join(', ')}`); // Throw error if invalid type
  }
  
  // Validate credit_limit
  if (parseFloat(credit_limit) < 0) {
    throw new ValidationError('Credit limit cannot be negative'); // Throw error if negative
  }
  
  // Validate price_list_id if provided
  if (price_list_id) {
    const priceList = await PriceList.findByPk(price_list_id); // Find price list
    if (!priceList) {
      throw new NotFoundError(`Price list with ID ${price_list_id} not found`); // Throw error if price list not found
    }
  }
  
  // Create customer
  const customer = await Customer.create({
    name: name.trim(), // Customer name (trimmed)
    customer_type, // Customer type
    email: email ? email.trim().toLowerCase() : null, // Email (trimmed and lowercased if provided)
    phone: phone ? phone.trim() : null, // Phone (trimmed if provided)
    kra_pin: kra_pin ? kra_pin.trim() : null, // KRA PIN (trimmed if provided)
    credit_limit: parseFloat(credit_limit), // Credit limit
    price_list_id: price_list_id || null, // Price list ID
    active: true, // Active status
  }); // Create customer
  
  logger.info(`Customer created: ${customer.id} - ${customer.name}`); // Log customer creation
  
  // Return created customer with associations
  return await getCustomer(customer.id); // Return customer with associations
};

/**
 * Get customer by ID
 * Retrieves a customer by ID
 * @param {number} customerId - Customer ID
 * @returns {Promise<Object>} Customer
 */
const getCustomer = async (customerId) => {
  // Validate customer ID
  if (!customerId) {
    throw new ValidationError('Customer ID is required'); // Throw error if ID missing
  }
  
  // Find customer with associations
  const customer = await Customer.findByPk(customerId, {
    include: [
      {
        model: PriceList, // Include price list
        as: 'priceList', // Use priceList alias
        required: false, // Left join (price list may be null)
      },
      {
        model: TaxExemption, // Include tax exemptions
        as: 'taxExemptions', // Use taxExemptions alias
        required: false, // Left join (tax exemptions may not exist)
      },
    ],
  });
  
  // Check if customer exists
  if (!customer) {
    throw new NotFoundError(`Customer with ID ${customerId} not found`); // Throw error if not found
  }
  
  // Return customer
  return customer; // Return customer
};

/**
 * Update customer
 * Updates an existing customer
 * @param {number} customerId - Customer ID
 * @param {Object} updateData - Update data
 * @returns {Promise<Object>} Updated customer
 */
const updateCustomer = async (customerId, updateData) => {
  // Validate customer ID
  if (!customerId) {
    throw new ValidationError('Customer ID is required'); // Throw error if ID missing
  }
  
  // Find customer
  const customer = await Customer.findByPk(customerId); // Find customer
  
  // Check if customer exists
  if (!customer) {
    throw new NotFoundError(`Customer with ID ${customerId} not found`); // Throw error if not found
  }
  
  // Extract update data
  const { name, customer_type, email, phone, kra_pin, credit_limit, price_list_id, active } = updateData; // Extract data
  
  // Validate customer_type if provided
  if (customer_type) {
    const validTypes = ['B2C', 'B2B']; // Valid customer types
    if (!validTypes.includes(customer_type)) {
      throw new ValidationError(`Customer type must be one of: ${validTypes.join(', ')}`); // Throw error if invalid type
    }
    customer.customer_type = customer_type; // Update customer type
  }
  
  // Update name if provided
  if (name !== undefined) {
    if (!name || name.trim() === '') {
      throw new ValidationError('Customer name cannot be empty'); // Throw error if empty name
    }
    customer.name = name.trim(); // Update name (trimmed)
  }
  
  // Update email if provided
  if (email !== undefined) {
    customer.email = email ? email.trim().toLowerCase() : null; // Update email (trimmed and lowercased if provided)
  }
  
  // Update phone if provided
  if (phone !== undefined) {
    customer.phone = phone ? phone.trim() : null; // Update phone (trimmed if provided)
  }
  
  // Update KRA PIN if provided
  if (kra_pin !== undefined) {
    customer.kra_pin = kra_pin ? kra_pin.trim() : null; // Update KRA PIN (trimmed if provided)
  }
  
  // Update credit_limit if provided
  if (credit_limit !== undefined) {
    if (parseFloat(credit_limit) < 0) {
      throw new ValidationError('Credit limit cannot be negative'); // Throw error if negative
    }
    customer.credit_limit = parseFloat(credit_limit); // Update credit limit
  }
  
  // Update price_list_id if provided
  if (price_list_id !== undefined) {
    if (price_list_id === null) {
      customer.price_list_id = null; // Set to null if explicitly provided
    } else {
      // Validate price list exists
      const priceList = await PriceList.findByPk(price_list_id); // Find price list
      if (!priceList) {
        throw new NotFoundError(`Price list with ID ${price_list_id} not found`); // Throw error if price list not found
      }
      customer.price_list_id = price_list_id; // Update price list ID
    }
  }
  
  // Update active status if provided
  if (active !== undefined) {
    customer.active = active; // Update active status
  }
  
  // Save customer
  await customer.save(); // Save customer
  
  logger.info(`Customer updated: ${customerId}`); // Log customer update
  
  // Return updated customer with associations
  return await getCustomer(customerId); // Return customer with associations
};

/**
 * Delete customer (soft delete)
 * Soft deletes a customer by setting active to false
 * @param {number} customerId - Customer ID
 * @returns {Promise<void>}
 */
const deleteCustomer = async (customerId) => {
  // Validate customer ID
  if (!customerId) {
    throw new ValidationError('Customer ID is required'); // Throw error if ID missing
  }
  
  // Find customer
  const customer = await Customer.findByPk(customerId); // Find customer
  
  // Check if customer exists
  if (!customer) {
    throw new NotFoundError(`Customer with ID ${customerId} not found`); // Throw error if not found
  }
  
  // Soft delete (set active to false)
  customer.active = false; // Set active to false
  await customer.save(); // Save customer
  
  logger.info(`Customer deleted (soft): ${customerId}`); // Log customer deletion
};

/**
 * List customers
 * Lists customers with optional filters and search
 * @param {Object} filters - Filter options (customer_type, active, price_list_id, search)
 * @param {Object} pagination - Pagination options (page, limit)
 * @returns {Promise<Object>} Paginated list of customers
 */
const listCustomers = async (filters = {}, pagination = {}) => {
  // Extract filters
  const {
    customer_type, // Customer type filter
    active, // Active status filter
    price_list_id, // Price list ID filter
    search, // Search query (searches name and KRA PIN)
  } = filters; // Extract filters
  
  // Extract pagination options
  const page = parseInt(pagination.page, 10) || 1; // Current page (default 1)
  const limit = parseInt(pagination.limit, 10) || 50; // Items per page (default 50)
  const offset = (page - 1) * limit; // Calculate offset
  
  // Build where clause
  const where = {}; // Initialize where clause
  
  // Add customer_type filter
  if (customer_type) {
    where.customer_type = customer_type; // Filter by customer type
  }
  
  // Add active filter
  if (active !== undefined) {
    where.active = active; // Filter by active status
  } else {
    // Default to only active customers if not specified
    where.active = true; // Filter by active status
  }
  
  // Add price_list_id filter
  if (price_list_id) {
    where.price_list_id = price_list_id; // Filter by price list ID
  }
  
  // Add search filter (searches name and KRA PIN)
  if (search && search.trim()) {
    const searchTerm = search.trim(); // Trim search term
    where[Op.or] = [
      { name: { [Op.like]: `%${searchTerm}%` } }, // Search in name
      { kra_pin: { [Op.like]: `%${searchTerm}%` } }, // Search in KRA PIN
    ]; // Add OR condition for search
  }
  
  // Find customers with pagination
  const { count, rows } = await Customer.findAndCountAll({
    where, // Where clause
    include: [
      {
        model: PriceList, // Include price list
        as: 'priceList', // Use priceList alias
        required: false, // Left join (price list may be null)
      },
    ],
    limit, // Limit results
    offset, // Offset results
    order: [['created_at', 'DESC']], // Order by creation date descending
  });
  
  // Calculate pagination metadata
  const totalPages = Math.ceil(count / limit); // Total pages
  const hasNextPage = page < totalPages; // Has next page
  const hasPrevPage = page > 1; // Has previous page
  
  // Return paginated results
  return {
    customers: rows, // Customers array
    pagination: {
      page, // Current page
      limit, // Items per page
      total: count, // Total count
      totalPages, // Total pages
      hasNextPage, // Has next page
      hasPrevPage, // Has previous page
    },
  };
};

/**
 * Search customers
 * Searches customers by name or KRA PIN
 * @param {string} searchTerm - Search term
 * @param {Object} options - Search options (limit)
 * @returns {Promise<Array>} Array of matching customers
 */
const searchCustomers = async (searchTerm, options = {}) => {
  // Validate search term
  if (!searchTerm || !searchTerm.trim()) {
    throw new ValidationError('Search term is required'); // Throw error if search term missing
  }
  
  // Extract options
  const limit = parseInt(options.limit, 10) || 20; // Limit results (default 20)
  const search = searchTerm.trim(); // Trim search term
  
  // Build where clause
  const where = {
    active: true, // Only active customers
    [Op.or]: [
      { name: { [Op.like]: `%${search}%` } }, // Search in name
      { kra_pin: { [Op.like]: `%${search}%` } }, // Search in KRA PIN
    ], // OR condition for search
  }; // Build where clause
  
  // Find customers
  const customers = await Customer.findAll({
    where, // Where clause
    include: [
      {
        model: PriceList, // Include price list
        as: 'priceList', // Use priceList alias
        required: false, // Left join
      },
    ],
    limit, // Limit results
    order: [['name', 'ASC']], // Order by name ascending
  });
  
  // Return customers
  return customers; // Return customers
};

// Export customer service functions
module.exports = {
  createCustomer,
  getCustomer,
  updateCustomer,
  deleteCustomer,
  listCustomers,
  searchCustomers,
};
