module Payments
  class PurchaseOrder < Payments::Base
    self.primary_attribute = :id

    attributes :id,
               :state,
               :state_details,
               :state_history,
               :payment_gateway,
               :user_id,
               :created_at,
               :is_gift,
               :platform,
               :product_id,
               :product_type,
               :gross_amount,
               :currency,
               :subscription_id,
               :purchase_order_payments,
               :purchase_order_line_items,
               :item_fulfillments,
               :refund_reasons,
               :subscription,
               :subscription_state,
               :subscription_states,
               :origin_id,
               :fraud_label

    STATES = [:success, :failed].freeze
    PLATFORMS = [:web, :android, :ios].freeze
    PAYMENT_GATEWAYS = [:recurly, :xsolla_v3, :zuora, :google_iab, :apple_iap].freeze
    PRODUCT_TYPES = [:sub, :gift_sub, :bits].freeze
    CSV_HEADERS = {
        id: "Id",
        state: "State",
        state_history: "State History",
        payment_gateway: "Payment Gateway",
        user_id: "User Id",
        created_at: "Created At",
        is_gift: "Is Gift",
        platform: "Platform",
        product_id: "Product Id",
        product_type: "Product Type",
        gross_amount: "Gross Amount",
        currency: "Currency",
        subscription_id: "Subscription Id",
        purchase_order_payments: "Purchase Order Payments",
        purchase_order_line_items: "Purchase Order Line Items",
        item_fulfillments: "Item Fulfillments",
        subscription: "Subscription",
        subscription_state: "Subscription State",
        subscription_states: "Subscription States",
        origin_id: "Origin Id"
    }.freeze

    SUB_TOKEN_CURRENCY = "sub_token"
    REFUND_FAILED = "refund_failed"
    EVERDEEN_REFUND_ORDER = "#{Settings.everdeen.endpoint}/twirp/code.justin.tv.revenue.everdeen.Everdeen/RefundOrder"

    def self.filter(params = {})
      sanitized_params = sanitize_filter_params(params)
      if sanitized_params.blank?
        return []
      end

      sanitized_params[:per_page] ||= 20

      # The full_details param allows us to access last_payment_date
      response = get '/admin_panel/purchase_orders', params: sanitized_params
      if response.success?
        res = response.body['purchase_orders'].map do |purchase_orders|
          from_attributes(purchase_orders)
        end
        # Set a high page number since we don't know the total count
        paginate res, total_pages: 100
      else
        return nil
      end
    end

    def self.find(id)
      return if id.blank?
      response = get "/admin_panel/purchase_orders/#{id}"

      if response.success?
        purchase_order = from_attributes(response.body["purchase_order"])
        purchase_order.purchase_order_payments = response.body["purchase_order_payments"]
        purchase_order.purchase_order_line_items = response.body["purchase_order_line_items"]
        purchase_order.subscription = response.body["subscription"]
        purchase_order.item_fulfillments = response.body["item_fulfillments"]
        purchase_order.refund_reasons = response.body["refund_reasons"]
        purchase_order.load_recipients

        purchase_order
      end
    end

    def self.count_for_purchaser(user_id)
      params = { user_id: user_id }
      response = get "/admin_panel/purchase_orders/count", params: params
      response.success? ? response.body["count"] : 0
    end

    def self.empty_response
      paginate []
    end

    def self.refund_as_fraud(id, admin_id)
      params = { order_ids: [id], reason: 'fraud', admin_id: admin_id }
      response = post "/admin/refund_orders", body: params
      response.success?
    end

    def self.label_fraud(id, label, admin_id)
      params = { label: label, label_type: 'fraud', entity_id: id, entity_type: 'purchase_order', admin_id: admin_id }
      response = post "/admin_panel/label", body: params
      response.success?
    end

    def self.mark_refunded_by_finance(id, admin_id)
      params = { order_ids: [id], admin_id: admin_id }
      response = put "/admin_panel/purchase_orders/#{id}/mark_refunded_by_finance", body: params
      return response.success?
    end

    def self.cancel_subscription_for_order(id)
      params = { benefits_directive: :CANCEL_BENEFITS, cancellation_directives: :FULL_REFUND }
      response = put "/admin_panel/purchase_orders/#{id}/cancel_subscription", body: params
      response.success?
    end

    def self.do_not_renew_subscription_for_order(id)
      params = { benefits_directive: :DO_NOT_RENEW_BENEFITS, cancellation_directives: :NO_REFUND }
      response = put "/admin_panel/purchase_orders/#{id}/cancel_subscription", body: params
      response.success?
    end

    def self.refund(id, refund_qty)
      purchase_order = find(id)
      if purchase_order.nil?
        return false
      end

      if !purchase_order.is_gift && refund_qty.to_i != purchase_order.gross_amount
        return false
      end

      if refund_qty.to_i <= 0 || refund_qty.to_i > purchase_order.gross_amount
        return false
      end

      params = { origin_id: purchase_order.origin_id, amount: purchase_order.gross_amount, refund_reason: "admin panel refund" }
      # PAY-5209: For gifts purchased with sub tokens, do not cancel benefits.
      if purchase_order.currency == SUB_TOKEN_CURRENCY && purchase_order.is_gift
        params[:benefits_directive] = :RETAIN_BENEFITS
      end
      response = post EVERDEEN_REFUND_ORDER, {
                    body: params.to_json,
                    headers: { 'content-type' => 'application/json' }
      }
      response.success?
    end

    def self.sanitize_filter_params(params)
      params.slice(
        :purchaser_id,
        :recipient_id,
        :state,
        :gift,
        :payment_gateway,
        :platform,
        :ext_subscription_id,
        :ext_transaction_id,
        :ext_subscription_id,
        :product_type,
        :created_at_start,
        :created_at_end,
        :page,
        :product_id,
        :subscription_id,
        :origin_id
      ).to_h.reject { |_, v| v.blank? }
    end

    def purchaser
      Twitch::User.find(user_id)
    end

    def load_recipients
      return nil if item_fulfillments.nil?

      item_fulfillments.each do |item_fulfillment|
        item_fulfillment["recipient"] = Twitch::User.find(item_fulfillment["recipient_id"]) unless item_fulfillment["recipient_id"].nil?
      end
    end

    def created_at
      Time.parse(@created_at) rescue nil
    end

    def product
      return @product unless @product.blank?
      return if product_id.nil?

      tp = Subscriptions::TicketProduct.find(product_id)
      @product = tp.errors.blank? ? tp : nil
    end

    def product_display_name
      product.try(:short_name) || product_id
    end

    def load_associations
      return Concurrent::Future.execute { product }, Concurrent::Future.execute { purchaser }
    end

    def to_csv_row
      CSV_HEADERS.keys.map { |key| self.attributes[key] }
    end
  end
end
