/**
 * Rental Duration Calculator with Hybrid Pricing
 *
 * Finds the optimal (cheapest) combination of pricing tiers for any rental duration.
 * Supports combining hours + days + weeks + months for best pricing.
 *
 * Example: 26 hours with $10/hr and $50/day
 * - Old logic: 2 days × $50 = $100
 * - New logic: 1 day × $50 + 2 hours × $10 = $70
 */
class RentalDurationCalculator {
  // Time constants in milliseconds
  static HOUR_MS = 60 * 60 * 1000;
  static DAY_MS = 24 * 60 * 60 * 1000;
  static WEEK_MS = 7 * 24 * 60 * 60 * 1000;
  static MONTH_MS = 30 * 24 * 60 * 60 * 1000;

  /**
   * Build available pricing tiers from item, sorted largest to smallest
   * @param {Object} item - Item object with pricing information
   * @returns {Array} Array of {name, price, durationMs} sorted largest to smallest
   */
  static buildPricingTiers(item) {
    const tiers = [];

    if (item.pricePerMonth && Number(item.pricePerMonth) > 0) {
      tiers.push({
        name: "month",
        price: Number(item.pricePerMonth),
        durationMs: this.MONTH_MS,
      });
    }

    if (item.pricePerWeek && Number(item.pricePerWeek) > 0) {
      tiers.push({
        name: "week",
        price: Number(item.pricePerWeek),
        durationMs: this.WEEK_MS,
      });
    }

    if (item.pricePerDay && Number(item.pricePerDay) > 0) {
      tiers.push({
        name: "day",
        price: Number(item.pricePerDay),
        durationMs: this.DAY_MS,
      });
    }

    if (item.pricePerHour && Number(item.pricePerHour) > 0) {
      tiers.push({
        name: "hour",
        price: Number(item.pricePerHour),
        durationMs: this.HOUR_MS,
      });
    }

    return tiers;
  }

  /**
   * Recursively calculate optimal price for remaining duration
   * Uses greedy approach: try largest tier first, then handle remainder with smaller tiers
   * Compares hybrid price vs round-up price and returns the cheaper option
   *
   * @param {number} remainingMs - Remaining duration in milliseconds
   * @param {Array} tiers - Available pricing tiers (largest to smallest)
   * @param {number} tierIndex - Current tier being considered
   * @returns {number} Optimal price for this duration
   */
  static calculateOptimalForDuration(remainingMs, tiers, tierIndex = 0) {
    // Base case: no remaining time
    if (remainingMs <= 0) {
      return 0;
    }

    // Base case: no more tiers available - round up with smallest available tier
    if (tierIndex >= tiers.length) {
      const smallestTier = tiers[tiers.length - 1];
      const unitsNeeded = Math.ceil(remainingMs / smallestTier.durationMs);
      return unitsNeeded * smallestTier.price;
    }

    const currentTier = tiers[tierIndex];
    const completeUnits = Math.floor(remainingMs / currentTier.durationMs);
    const remainder = remainingMs - completeUnits * currentTier.durationMs;

    if (completeUnits > 0) {
      // Option 1: Use complete units of current tier + handle remainder with smaller tiers
      const currentCost = completeUnits * currentTier.price;
      const remainderCost = this.calculateOptimalForDuration(
        remainder,
        tiers,
        tierIndex + 1
      );
      const hybridCost = currentCost + remainderCost;

      // Option 2: Round up to one more unit of current tier (may be cheaper if remainder cost is high)
      const roundUpCost = (completeUnits + 1) * currentTier.price;

      // Return the cheaper option when there's a remainder to handle
      return remainder > 0 ? Math.min(hybridCost, roundUpCost) : currentCost;
    } else {
      // Duration too small for this tier, try next smaller tier
      return this.calculateOptimalForDuration(remainingMs, tiers, tierIndex + 1);
    }
  }

  /**
   * Calculate rental cost based on duration and item pricing tiers
   * Uses hybrid pricing to find the optimal (cheapest) combination of tiers
   *
   * @param {Date|string} startDateTime - Rental start date/time
   * @param {Date|string} endDateTime - Rental end date/time
   * @param {Object} item - Item object with pricing information
   * @returns {number} Total rental cost
   */
  static calculateRentalCost(startDateTime, endDateTime, item) {
    const start = new Date(startDateTime);
    const end = new Date(endDateTime);

    // Calculate total duration in milliseconds
    const durationMs = end.getTime() - start.getTime();

    if (durationMs <= 0) {
      return 0;
    }

    // Build available pricing tiers
    const tiers = this.buildPricingTiers(item);

    // No pricing tiers = free item
    if (tiers.length === 0) {
      return 0;
    }

    // Calculate optimal price using hybrid tier combination
    const optimalPrice = this.calculateOptimalForDuration(durationMs, tiers);

    return parseFloat(optimalPrice.toFixed(2));
  }
}

module.exports = RentalDurationCalculator;
