module JamRuby
  class ShoppingCart < ActiveRecord::Base

    # just a normal purchase; used on the description field of a recurly adjustment
    PURCHASE_NORMAL = 'purchase-normal'
    # a free purchase; used on the description field of a recurly adjustment
    PURCHASE_FREE = 'purchase-free'
    # a techinicality of Recurly; we create a free-credit adjustment to balance out the free purchase adjustment
    PURCHASE_FREE_CREDIT = 'purchase-free-credit'

    PURCHASE_REASONS = [PURCHASE_NORMAL, PURCHASE_FREE, PURCHASE_FREE_CREDIT]

    JAMTRACK_FULL = 'full'
    JAMTRACK_STREAM = 'stream'
    JAMTRACK_DOWNLOAD = 'download'
    JAMTRACK_VARIANTS = ['full', 'stream', 'download']

    attr_accessible :quantity, :cart_type, :product_info

    attr_accessor :skip_mix_check

    validates_uniqueness_of     :cart_id, scope: [:cart_type, :user_id, :anonymous_user_id]

    belongs_to :user, :inverse_of => :shopping_carts, :class_name => "JamRuby::User", :foreign_key => "user_id"

    validates :cart_id, presence: true
    validates :cart_type, presence: true
    validates :cart_class_name, presence: true
    validates :marked_for_redeem, numericality: {only_integer: true}
    validates :variant,            inclusion: {in: [nil, JAMTRACK_FULL, JAMTRACK_STREAM, JAMTRACK_DOWNLOAD]}

    #validate  :not_mixed

    default_scope { order('created_at DESC') }

    def product_info(instance = nil)
      product = self.cart_product
      data = {type: cart_type, name: product.name, price: product.variant_price(variant), product_id: cart_id, plan_code: product.plan_code, real_price: real_price(product), total_price: total_price(product), quantity: quantity, marked_for_redeem: marked_for_redeem, free: free?, sales_region: product.sales_region, sale_display:product.sale_display(variant), allow_free: allow_free(product)} unless product.nil?
      if data && instance
        data.merge!(instance.product_info)
      end
      data
    end

    # multiply quantity   by  price
    def total_price(product)
      quantity * product.variant_price(variant)
    end

    def purchasing_downloadable_rights?
      is_jam_track? && (variant == ShoppingCart::JAMTRACK_DOWNLOAD || variant == ShoppingCart::JAMTRACK_FULL)
    end

    # multiply (quantity - redeemable)  by  price
    def real_price(product)
      (quantity - marked_for_redeem) * product.variant_price(variant)
    end

    def allow_free(product)
      if(product.is_a?(JamTrack))
        product.allow_free
      else
        false
      end
    end

    def not_mixed

      return if @skip_mix_check
      existing_carts = []
      this_user = any_user()

      if this_user
        existing_carts = this_user.shopping_carts
      end

      existing_carts = existing_carts.to_a
      existing_carts << self

      if Sale.is_mixed(existing_carts)
        if free?
          errors.add(:base, "You can not add a free JamTrack to a cart with non-free items.  Please clear out your cart.")
          return false
        else
          errors.add(:base, "You can not add a non-free JamTrack to a cart containing free items.  Please clear out your cart.")
          return false
        end
      end

      false
    end

    def cart_product
      self.cart_class_name.classify.constantize.find_by_id(self.cart_id) unless self.cart_class_name.blank?
    end

    def redeem(mark_redeem)
      self.marked_for_redeem = mark_redeem ? 1 : 0
    end

    def free?
      marked_for_redeem == quantity
    end

    def any_user
      if user
        user
      elsif anonymous_user_id
        AnonymousUser.new(anonymous_user_id, nil)
      else
        nil
      end
    end

    def self.create(user, product, quantity = 1, mark_redeem = false, variant = nil)

      cart = ShoppingCart.new
      if user.is_a?(User)
        cart.user = user
      else
        cart.anonymous_user_id = user.id
      end

      cart.cart_type = product.class::PRODUCT_TYPE

      if cart.cart_type == JamTrack::PRODUCT_TYPE && variant.nil?
        cart.variant = JAMTRACK_STREAM # default to jamtrack 'stream'
      else
        cart.variant = variant
      end


      cart.cart_class_name = product.class.name
      cart.cart_id = product.id
      cart.quantity = quantity
      cart.redeem(mark_redeem)
      cart.save
      cart
    end

    def is_jam_track?
      cart_type == JamTrack::PRODUCT_TYPE
    end

    def is_gift_card?
      cart_type == GiftCardType::PRODUCT_TYPE
    end

    def is_lesson?
      cart_type == LessonPackageType::PRODUCT_TYPE
    end

    # returns an array of adjustments for the shopping cart
    def create_adjustment_attributes(current_user)
      raise "not a jam track or gift card" unless is_jam_track? || is_gift_card?

      info = self.product_info

      if free?
        # create the credit, then the pseudo charge
        []
      else

        [
            {
                accounting_code: PURCHASE_NORMAL,
                currency: 'USD',
                unit_amount_in_cents: (info[:total_price] * 100).to_i,
                description: info[:sale_display],
                tax_exempt: false
            }
        ]
      end
    end

    def self.move_to_user(user, anonymous_user, shopping_carts)
      shopping_carts.each do |shopping_cart|
        if shopping_cart.is_jam_track?
          mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(user)
          cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, mark_redeem, shopping_cart.variant)
        else
          cart = ShoppingCart.create(user, shopping_cart.cart_product, shopping_cart.quantity, false, shopping_cart.variant)
        end

      end

      anonymous_user.destroy_all_shopping_carts
    end

    def self.is_product_purchase?(adjustment)
      (adjustment[:accounting_code].include?(PURCHASE_FREE) || adjustment[:accounting_code].include?(PURCHASE_NORMAL)) && !adjustment[:accounting_code].include?(PURCHASE_FREE_CREDIT)
    end

    # recurly_adjustment is a Recurly::Adjustment (http://www.rubydoc.info/gems/recurly/Recurly/Adjustment)
    # this asks, 'is this a pending adjustment?' AND 'was this adjustment created by the server (vs manually by someone -- we should leave those alone).'
    def self.is_server_pending_adjustment?(recurly_adjustment)
      recurly_adjustment.state == 'pending' && (recurly_adjustment.accounting_code.include?(PURCHASE_FREE) || recurly_adjustment.accounting_code.include?(PURCHASE_NORMAL) || recurly_adjustment.accounting_code.include?(PURCHASE_FREE_CREDIT))
    end

    # if the user has a redeemable jam_track still on their account, then also check if any shopping carts have already been marked.
    # if no shpping carts have been marked, then mark it redeemable
    # should be wrapped in a TRANSACTION
    def self.user_has_redeemable_jam_track?(any_user)

      if any_user.has_redeemable_jamtrack || any_user.gifted_jamtracks > 0

        free_in_cart = 0

        any_user.shopping_carts.each do |shopping_cart|
          # but if we find any shopping cart item already marked for redeem, then back out of mark_redeem=true
          if shopping_cart.cart_type == JamTrack::PRODUCT_TYPE
            free_in_cart += shopping_cart.marked_for_redeem
          end
        end

        any_user.free_jamtracks  > free_in_cart
      else
        false
      end
    end

    # adds a jam_track to cart, checking for promotions
    def self.add_jam_track_to_cart(any_user, jam_track, variant = JAMTRACK_FULL)
      cart = nil

      if variant.nil?
        variant = JAMTRACK_FULL
      end

      ShoppingCart.transaction do

        # if clear
        if any_user.shopping_carts.length == 1 && any_user.shopping_carts[0].product_info[:allow_free] && (any_user.has_redeemable_jamtrack && any_user.gifted_jamtracks == 0) && jam_track.allow_free && any_user.free_jamtracks > 0 # clear
          # if you are an anonymous user, we make sure there is nothing else in your shopping cart ... keep it clean for the 'new user rummaging around for a freebie scenario'
          any_user.destroy_jam_track_shopping_carts
          any_user.reload
        end

        mark_redeem = jam_track.allow_free ? ShoppingCart.user_has_redeemable_jam_track?(any_user) : false
        cart = ShoppingCart.create(any_user, jam_track, 1, mark_redeem, variant)
      end
      any_user.reload
      cart
    end

    def self.add_item_to_cart(any_user, item)
      cart = nil
      ShoppingCart.transaction do
        cart = ShoppingCart.create(any_user, item, 1, false)
      end
      cart
    end

    # deletes a jam track from the shopping cart, updating redeem flag as necessary
    def self.remove_jam_track_from_cart(any_user, cart)
      ShoppingCart.transaction do
        cart.destroy

        # so that user.shopping_carts reflects truth
        any_user.reload

        # check if we should move the redemption around automatically
        mark_redeem = ShoppingCart.user_has_redeemable_jam_track?(any_user)

        carts = any_user.shopping_carts

        # if we find any carts on the account that are not redeemed, mark first one redeemable
        if mark_redeem && carts.length > 0
          carts.each do |cart|
            if cart.marked_for_redeem == 0
              if cart.quantity > 1
                raise 'unknown situation for redeemption juggling'
              end
              cart.redeem(mark_redeem)
              cart.save
              break
            end
          end
        end
      end
    end

    def self.remove_item_from_cart(any_user, cart)
      ShoppingCart.transaction do
        cart.destroy
      end
    end

    # if the number of items in the shopping cart is less than gifted_jamtracks on the user, then fix them all up
    def self.apply_gifted_jamtracks(user)
      jam_track_carts = user.shopping_carts.where(cart_type:JamTrack::PRODUCT_TYPE)

      if jam_track_carts.count > user.gifted_jamtracks
        # just whack everything in their shopping cart
        user.destroy_all_shopping_carts
        return
      end

      jam_track_carts.each do |cart|
        cart.skip_mix_check = true
        cart.marked_for_redeem = 1
        cart.save!
      end
    end

    def port(user, anonymous_user)

      ShoppingCart.transaction do
        move_to_user(user, anonymous_user, anonymous_user.shopping_carts)
      end
    end
  end
end