require_dependency 'stats/client'

module Finance
  # Mixin for Cinstance to get the variable costs calculations.
  module VariableCost

    # Bills variable costs for the intersection of "period" and
    # variable_cost_paid_until.
    # - variable costs are billed for complete days.
    # - variable_cost_paid_until marks the exact time of paid variable costs.
    # - intersection bills from the beginning of the first day till the end of the last day
    def bill_for_variable(period, invoice, plan_to_bill = self.plan)
      # TODO: this makes the bill_for method dependent on Time.zone.now
      # so it should be handled differently
      #
      return false if trial?

      transaction do

        if variable_cost_paid_until.to_date < period.end
          intersection = intersect_with_unpaid_period(period, variable_cost_paid_until)
          # intersect_with_unpaid_period returns the intersection in
          # time, but it rounds from beginning of day of the first day
          # till end of the day of last day

          bill_variable_fee_for(intersection, invoice, plan_to_bill)

          self.variable_cost_paid_until = period.end
        else
          Rails.logger.info("Skipping contract #{self.id} - already paid until #{variable_cost_paid_until}")
        end

        # no validation because our DB has broken data
        # TODO: cleanup DB and add validations
        if invoice.used?
          self.save(:validate => false)
          true
        else
          false
        end
      end
    end

    # Return the variable cost for the given period.
    #
    # == Arguments
    #
    #   +period+ - Something that quacks like a range of Time-like objects.
    #
    # == Returns
    #
    # a hash of {metric => cost}, where +metric+ is a Metric instance and cost is a number.
    #
    # == TODO
    #
    # period.end is ignored currently, and it is silently assumed that period.begin is the
    # beginning of the month. Support also other periods.
    #
    # TODO: refactor
    def calculate_variable_cost(period, plan_to_bill = self.plan)
      data = Stats::Client.new(self)
      values = {}

      costs = plan_to_bill.pricing_rules.inject({}) do |memo, pricing_rule|
        metric = pricing_rule.metric

        values[metric] ||= data.total(:period   => period,
                                      :timezone => provider_account.timezone,
                                      :metric   => metric)

        memo[metric] ||= 0
        memo[metric] += pricing_rule.cost_for_value(values[metric])
        memo
      end
      [ values, costs ]
    end

    protected

    def bill_variable_fee_for(period, invoice, plan_to_bill)
      quantity, variable_cost = calculate_variable_cost(period, plan_to_bill)
      variable_cost = variable_cost.select { |metric,cost|  cost.nonzero? }.sort_by { |m,cost| -cost }

      variable_cost.each do |metric,cost|

      invoice.bill do |li|
        li = li.becomes!(LineItem::VariableCost)
        li.cost = cost
        li.name = metric.friendly_name
        li.description = period.to_time_range.to_s
        li.quantity = quantity[metric]
        li.contract = self
        li.plan_id = plan_to_bill.id
        li.metric = metric
      end
      end
    end

  end
end
