/**
 * Product Prices Service
 * Business logic for product price management
 */

// Import ProductPrice and related models
const { ProductPrice, PriceList, Product, Customer } = require('../../../models'); // ProductVariant removed - variants not used
// 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 product price
 * Creates a new product price in a price list
 * Supports per-product pricing (product_id required)
 * Per-group pricing can be achieved using different price lists for different product groups/categories
 * @param {Object} priceData - Price data (price_list_id, product_id, price, min_quantity, max_quantity, effective_from, effective_to)
 * @returns {Promise<Object>} Created product price
 */
const createProductPrice = async (priceData) => {
  // Extract price data
  const {
    price_list_id,
    product_id,
    price,
    min_quantity = 1,
    max_quantity = null,
    effective_from = new Date(),
    effective_to = null,
    active = true,
  } = priceData; // Extract data
  
  // Validate required fields
  if (!price_list_id || !product_id || price === undefined) {
    throw new ValidationError('Price list ID, product ID, and price are required'); // Throw error if missing required fields
  }
  
  // Validate price
  if (price < 0) {
    throw new ValidationError('Price cannot be negative'); // Throw error if negative price
  }
  
  // Validate quantity range
  if (min_quantity < 0) {
    throw new ValidationError('Minimum quantity cannot be negative'); // Throw error if negative min quantity
  }
  
  if (max_quantity !== null && max_quantity < min_quantity) {
    throw new ValidationError('Maximum quantity must be greater than or equal to minimum quantity'); // Throw error if invalid range
  }
  
  // Validate date range
  if (effective_to && new Date(effective_to) < new Date(effective_from)) {
    throw new ValidationError('Effective to date must be after effective from date'); // Throw error if invalid date range
  }
  
  // Verify price list exists
  const priceList = await PriceList.findByPk(price_list_id); // Find price list by ID
  if (!priceList) {
    throw new NotFoundError('Price list not found'); // Throw error if price list doesn't exist
  }
  
  // Verify product exists
  const product = await Product.findByPk(product_id); // Find product by ID
  if (!product) {
    throw new NotFoundError('Product not found'); // Throw error if product doesn't exist
  }
  
  // Variants removed - pricing is at product level only
  
  // Check for overlapping price entries (same product/price list with overlapping quantity ranges and dates)
  // Note: We allow overlapping prices but log a warning (business may want multiple prices for different scenarios)
  const overlappingPrice = await ProductPrice.findOne({
    where: {
      price_list_id, // Match price list ID
      product_id, // Match product ID
      // variant_id removed - variants not used
      active: true, // Only check active prices
      // Overlapping quantity ranges
      [Op.and]: [
        { min_quantity: { [Op.lte]: max_quantity || 999999 } }, // New min is within existing range
        {
          [Op.or]: [
            { max_quantity: { [Op.gte]: min_quantity } }, // Existing max overlaps with new min
            { max_quantity: null }, // No max quantity (infinite)
          ],
        },
      ],
      // Overlapping date ranges
      [Op.and]: [
        { effective_from: { [Op.lte]: effective_to || new Date('2099-12-31') } }, // Existing from is before new to
        {
          [Op.or]: [
            { effective_to: { [Op.gte]: effective_from } }, // Existing to is after new from
            { effective_to: null }, // No to date (ongoing)
          ],
        },
      ],
    },
  });
  
  if (overlappingPrice) {
    // Allow overlapping prices but log warning (business may want multiple prices for different scenarios)
    logger.warn(`Overlapping price entry found for product ${product_id}, price list ${price_list_id}`);
  }
  
  // Create product price
  const productPrice = await ProductPrice.create({
    price_list_id, // Set price list ID
    product_id, // Set product ID
    // variant_id removed - variants not used
    price, // Set price
    min_quantity, // Set minimum quantity
    max_quantity, // Set maximum quantity (can be null)
    effective_from: new Date(effective_from), // Set effective from date
    effective_to: effective_to ? new Date(effective_to) : null, // Set effective to date (can be null)
    active, // Set active status
  });
  
  // Reload product price with associations
  const priceWithDetails = await ProductPrice.findByPk(productPrice.id, {
    include: [
      {
        model: PriceList, // Include price list details
        as: 'priceList', // Use priceList alias
        attributes: ['id', 'name', 'code'], // Select specific attributes
      },
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
        attributes: ['id', 'name', 'sku'], // Select specific attributes
      },
      // ProductVariant removed - variants not used
    ],
  });
  
  // Log product price creation
  logger.info(`Product price created: product_id=${product_id}, price_list_id=${price_list_id}`, {
    priceId: productPrice.id,
    productId: product_id,
    priceListId: price_list_id,
    price,
  });
  
  // Return created product price
  return priceWithDetails; // Return product price
};

/**
 * Get product price by ID
 * Retrieves a product price with details
 * @param {number} priceId - Product price ID
 * @returns {Promise<Object>} Product price
 */
const getProductPrice = async (priceId) => {
  // Find product price by ID
  const productPrice = await ProductPrice.findByPk(priceId, {
    include: [
      {
        model: PriceList, // Include price list details
        as: 'priceList', // Use priceList alias
      },
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
      },
      // ProductVariant removed - variants no longer exist
    ],
  });
  
  // If product price not found, throw error
  if (!productPrice) {
    throw new NotFoundError('Product price not found'); // Throw error
  }
  
  // Return product price
  return productPrice; // Return product price
};

/**
 * Update product price
 * Updates an existing product price
 * @param {number} priceId - Product price ID
 * @param {Object} updateData - Update data
 * @returns {Promise<Object>} Updated product price
 */
const updateProductPrice = async (priceId, updateData) => {
  // Find product price
  const productPrice = await ProductPrice.findByPk(priceId); // Find product price by ID
  if (!productPrice) {
    throw new NotFoundError('Product price not found'); // Throw error if product price doesn't exist
  }
  
  // Extract update data
  const { price, min_quantity, max_quantity, effective_from, effective_to, active } = updateData; // Extract data
  
  // Validate price if being updated
  if (price !== undefined && price < 0) {
    throw new ValidationError('Price cannot be negative'); // Throw error if negative price
  }
  
  // Validate quantity range if being updated
  const finalMinQuantity = min_quantity !== undefined ? min_quantity : productPrice.min_quantity; // Use updated or existing
  const finalMaxQuantity = max_quantity !== undefined ? max_quantity : productPrice.max_quantity; // Use updated or existing
  
  if (finalMaxQuantity !== null && finalMaxQuantity < finalMinQuantity) {
    throw new ValidationError('Maximum quantity must be greater than or equal to minimum quantity'); // Throw error if invalid range
  }
  
  // Validate date range if being updated
  const finalEffectiveFrom = effective_from ? new Date(effective_from) : productPrice.effective_from; // Use updated or existing
  const finalEffectiveTo = effective_to !== undefined ? (effective_to ? new Date(effective_to) : null) : productPrice.effective_to; // Use updated or existing
  
  if (finalEffectiveTo && finalEffectiveTo < finalEffectiveFrom) {
    throw new ValidationError('Effective to date must be after effective from date'); // Throw error if invalid date range
  }
  
  // Update product price
  await productPrice.update(updateData); // Update product price
  
  // Reload product price with associations
  const updatedPrice = await ProductPrice.findByPk(priceId, {
    include: [
      {
        model: PriceList, // Include price list details
        as: 'priceList', // Use priceList alias
      },
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
      },
      // ProductVariant removed - variants no longer exist
    ],
  });
  
  // Log product price update
  logger.info(`Product price updated: ${priceId}`, {
    priceId,
    updateData,
  });
  
  // Return updated product price
  return updatedPrice; // Return product price
};

/**
 * Delete product price
 * Deletes a product price
 * @param {number} priceId - Product price ID
 * @returns {Promise<void>}
 */
const deleteProductPrice = async (priceId) => {
  // Find product price
  const productPrice = await ProductPrice.findByPk(priceId); // Find product price by ID
  if (!productPrice) {
    throw new NotFoundError('Product price not found'); // Throw error if product price doesn't exist
  }
  
  // Delete product price
  await productPrice.destroy(); // Delete product price
  
  // Log product price deletion
  logger.info(`Product price deleted: ${priceId}`, {
    priceId,
  });
};

/**
 * List product prices with filters
 * Retrieves paginated list of product prices
 * @param {Object} options - Query options
 * @param {number} options.page - Page number (default: 1)
 * @param {number} options.limit - Items per page (default: 10)
 * @param {number|null} options.priceListId - Filter by price list ID
 * @param {number|null} options.productId - Filter by product ID
 * @param {boolean|null} options.active - Filter by active status
 * @returns {Promise<Object>} Paginated product prices list
 */
const listProductPrices = async (options = {}) => {
  // Extract options with defaults
  const {
    page = 1,
    limit = 10,
    priceListId = null,
    productId = null,
    active = null,
  } = options; // Extract and set defaults (variantId removed - variants not used)
  
  // Build where clause
  const where = {}; // Initialize where clause
  
  // Add price list ID filter if provided
  if (priceListId) {
    where.price_list_id = priceListId; // Filter by price list ID
  }
  
  // Add product ID filter if provided
  if (productId) {
    where.product_id = productId; // Filter by product ID
  }
  
  // Variant filtering removed - variants not used
  
  // Add active filter if provided
  if (active !== null) {
    where.active = active === true || active === 'true'; // Filter by active status
  }
  
  // Add effective date filter (only show prices effective now)
  const now = new Date(); // Current date
  const effectiveDateFilter = [
    { effective_from: { [Op.lte]: now } }, // Effective from date is before or equal to now
    {
      [Op.or]: [
        { effective_to: { [Op.gte]: now } }, // Effective to date is after or equal to now
        { effective_to: null }, // Or no end date (ongoing)
      ],
    },
  ];
  
  // Merge effective date filter with existing where clause
  if (where[Op.and]) {
    where[Op.and] = [...where[Op.and], ...effectiveDateFilter]; // Merge with existing AND conditions
  } else {
    where[Op.and] = effectiveDateFilter; // Set AND conditions
  }
  
  // Calculate offset for pagination
  const offset = (page - 1) * limit; // Calculate offset
  
  // Build query options
  const queryOptions = {
    where, // Apply where clause
    include: [
      {
        model: PriceList, // Include price list details
        as: 'priceList', // Use priceList alias
        attributes: ['id', 'name', 'code'], // Select specific attributes
      },
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
        attributes: ['id', 'name', 'sku'], // Select specific attributes
      },
      // ProductVariant removed - variants not used
    ],
    limit: parseInt(limit), // Set limit
    offset: parseInt(offset), // Set offset
    order: [['effective_from', 'DESC'], ['min_quantity', 'ASC']], // Order by effective date then min quantity
  };
  
  // Execute query to get prices and total count
  const { count, rows } = await ProductPrice.findAndCountAll(queryOptions); // Get prices with count
  
  // Calculate total pages
  const totalPages = Math.ceil(count / limit); // Calculate total pages
  
  // Return paginated results
  return {
    prices: rows, // Product price records
    pagination: {
      page: parseInt(page), // Current page
      limit: parseInt(limit), // Items per page
      total: count, // Total items
      totalPages, // Total pages
    },
  };
};

/**
 * Get price for product
 * Retrieves the applicable price for a product in a price list
 * Supports per-product pricing (product_id required)
 * Per-group pricing can be achieved using different price lists for different product groups/categories
 * @param {number} productId - Product ID
 * @param {number} quantity - Quantity (for quantity-based pricing)
 * @param {number|null} priceListId - Price list ID (optional, uses default if not provided)
 * @returns {Promise<Object|null>} Product price or null
 */
const getPrice = async (productId, quantity = 1, priceListId = null) => {
  // Get price list ID (use provided or default)
  let finalPriceListId = priceListId; // Use provided price list ID
  
  if (!finalPriceListId) {
    // Get default price list
    let defaultPriceList = await PriceList.findOne({
      where: { is_default: true, active: true }, // Match default and active
    });
    
    // Auto-create default price list if none exists
    if (!defaultPriceList) {
      // Check if any price list exists
      const anyPriceList = await PriceList.findOne({
        where: { active: true },
      });
      
      if (!anyPriceList) {
        // No price lists exist, create a default retail price list
        const priceListService = require('./priceLists');
        defaultPriceList = await priceListService.createPriceList({
          name: 'Default Retail Price List',
          code: 'DEFAULT_RETAIL',
          price_list_type: 'RETAIL',
          currency: 'KES',
          description: 'Automatically created default price list for retail pricing',
          is_default: true,
        });
        
        const logger = require('../../../utils/logger');
        logger.info('Auto-created default price list', {
          priceListId: defaultPriceList.id,
          name: defaultPriceList.name,
          code: defaultPriceList.code,
        });
      } else {
        // Price lists exist but none is marked as default, set the first one as default
        await PriceList.update(
          { is_default: true },
          { where: { id: anyPriceList.id } }
        );
        anyPriceList.is_default = true;
        defaultPriceList = anyPriceList;
        
        const logger = require('../../../utils/logger');
        logger.info('Auto-set first price list as default', {
          priceListId: defaultPriceList.id,
          name: defaultPriceList.name,
        });
      }
    }
    
    finalPriceListId = defaultPriceList.id; // Use default price list ID
  }
  
  // Build where clause
  const where = {
    price_list_id: finalPriceListId, // Match price list ID
    product_id: productId, // Match product ID
    // variant_id removed - variants not used
    active: true, // Only active prices
    min_quantity: { [Op.lte]: quantity }, // Minimum quantity is less than or equal to requested quantity
    [Op.or]: [
      { max_quantity: { [Op.gte]: quantity } }, // Maximum quantity is greater than or equal to requested quantity
      { max_quantity: null }, // Or no maximum quantity (infinite)
    ],
    // Effective date filter
    effective_from: { [Op.lte]: new Date() }, // Effective from date is before or equal to now
    [Op.or]: [
      { effective_to: { [Op.gte]: new Date() } }, // Effective to date is after or equal to now
      { effective_to: null }, // Or no end date (ongoing)
    ],
  };
  
  // Find matching price
  // Order by: min_quantity DESC (highest quantity tier first) for quantity-based pricing
  const productPrice = await ProductPrice.findOne({
    where, // Apply where clause
    order: [
      ['min_quantity', 'DESC'], // Highest quantity tier first
    ],
    include: [
      {
        model: PriceList, // Include price list details
        as: 'priceList', // Use priceList alias
        attributes: ['id', 'name', 'code'], // Select specific attributes
      },
      {
        model: Product, // Include product details
        as: 'product', // Use product alias
        attributes: ['id', 'name', 'sku'], // Select specific attributes
      },
      // ProductVariant removed - variants not used
    ],
  });
  
  // Return product price (or null if not found)
  return productPrice; // Return product price
};

/**
 * Get price for customer
 * Retrieves the applicable price for a product for a specific customer
 * Note: Since customer registration is removed, all sales are walk-in (customerId = null)
 * This function is kept for future customer-specific pricing via price lists
 * @param {number} productId - Product ID
 * @param {number} quantity - Quantity (for quantity-based pricing)
 * @param {number|null} customerId - Customer ID (optional, null for walk-in customers - all current sales)
 * @returns {Promise<Object|null>} Product price or null
 */
const getPriceForCustomer = async (productId, quantity = 1, customerId = null) => {
  // Get price list ID for customer
  let priceListId = null; // Initialize price list ID
  
  if (customerId) {
    // Get customer
    const customer = await Customer.findByPk(customerId); // Find customer by ID
    if (customer && customer.price_list_id) {
      priceListId = customer.price_list_id; // Use customer's price list
    }
  }
  
  // Get price using customer's price list or default
  return await getPrice(productId, quantity, priceListId); // Get price
};

// Export service functions
module.exports = {
  createProductPrice, // Create product price function
  getProductPrice, // Get product price function
  updateProductPrice, // Update product price function
  deleteProductPrice, // Delete product price function
  listProductPrices, // List product prices function
  getPrice, // Get price function
  getPriceForCustomer, // Get price for customer function
};

