module Payments
  class PurchaseOrdersController < Payments::BaseController
    before_action :set_purchase_order, only: [:show]

    def index
      authorize PurchaseOrder

      if params[:async]
        if email_purchase_orders_report(params.except(:async))
          flash[:success] = "Successfully emailed purchase orders CSV to #{current_user.email}."
        else
          flash[:error] = "Failed to email purchase orders to #{current_user.email}."
        end
        redirect_back
      else
        search_by_transaction_id unless params[:ext_transaction_id].blank?
        search_by_purchase_order_id unless params[:purchase_order_id].blank?
        search_by_origin_id unless params[:origin_id].blank?

        search_purchase_orders

        # TODO: Remove this block after deprecating the old download as CSV method.
        respond_to do |format|
          format.html
          format.csv { send_data to_csv, filename: "purchase-orders-#{Date.today}.csv" }
        end

      end
      track_purchase_orders_search!(params: params)
    end

    def show
    end

    def label_fraud
      authorize PurchaseOrder

      fraud_label = params.require(:is_fraud) == 'true' ? 'fraud' : 'not_fraud'

      if PurchaseOrder.label_fraud(params[:purchase_order_id], fraud_label, current_user.ldap_login)
        flash[:success] = "Order labeled as #{fraud_label}"

        track_action! trackable_request
          .assemble_an
          .admin_panel_button(
            button: "purchase_orders_label_#{fraud_label}",
            purchase_order_id: params[:purchase_order_id]
          )
      else
        flash[:error] = "Failed to label order"
      end
      redirect_back
    end

    def refund_fraud
      authorize PurchaseOrder

      if PurchaseOrder.refund_as_fraud(params[:purchase_order_id], current_user.ldap_login)
        flash[:success] = "Order refunded"

        track_action! trackable_request
          .assemble_an
          .admin_panel_button(
            button: "purchase_orders_refund_as_fraud",
            purchase_order_id: params[:purchase_order_id]
          )
      else
        flash[:error] = "Failed to refund"
      end
      redirect_back
    end

    def mark_refunded_by_finance
      authorize PurchaseOrder
      if PurchaseOrder.mark_refunded_by_finance(params[:purchase_order_id], current_user.ldap_login)
        flash[:success] = "Order marked as manually refunded"
      else
        flash[:error] = "Failed to mark as manually refunded"
      end
      redirect_back
    end

    def refund
      authorize PurchaseOrder

      form = params[:payments_purchase_order]
      refund_quantity = form[:gross_amount]
      original_amount = form[:original_amount]
      is_gift = form[:is_gift]

      if is_gift.to_s == "true" && (refund_quantity.to_i <= 0 || refund_quantity.to_i > original_amount.to_i)
        flash[:error] = "Invalid Token Qty to Refund"
      elsif refund_quantity.to_i != original_amount.to_i
        flash[:error] = "Cannot do Partial Refunds for non gifts"
      elsif PurchaseOrder.refund(params[:purchase_order_id], refund_quantity)
        flash[:success] = "Order refunded"
      else
        flash[:error] = "Failed to refund"
      end
      redirect_back
    end

    def cancel
      authorize PurchaseOrder

      if PurchaseOrder.cancel_subscription_for_order(params[:purchase_order_id])
        flash[:success] = "Subscription canceled and refunded"

        track_action! trackable_request
          .assemble_an
          .admin_panel_button(
            button: "purchase_orders_terminate",
            purchase_order_id: params[:purchase_order_id]
          )
      else
        flash[:error] = "Failed to cancel purchase order"
      end
      redirect_back
    end

    def do_not_renew
      authorize PurchaseOrder

      if PurchaseOrder.do_not_renew_subscription_for_order(params[:purchase_order_id])
        flash[:success] = "Subscription will not renew"

        track_action! trackable_request
          .assemble_an
          .admin_panel_button(
            button: "purchase_orders_do_not_renew",
            purchase_order_id: params[:purchase_order_id]
          )
      else
        flash[:error] = "Failed to DNR purchase order"
      end
      redirect_back
    end

    protected

    def set_purchase_order
      authorize PurchaseOrder
      @purchase_order = PurchaseOrder.find(params[:id] || params[:purchase_order_id])

      if @purchase_order.blank?
        flash[:error] = "No purchase order found with ID: #{params[:id]}"
        redirect_to payments_purchase_orders_path
        return
      end

      unless @purchase_order.subscription.blank?
        @subscription_purchase_orders = PurchaseOrder.filter(
          subscription_id: @purchase_order.subscription["id"],
          per_page: 'all'
        )
        if @subscription_purchase_orders.nil?
          flash[:error] = "Error searching for purchase order found with ID: #{params[:id]}. Please try again"
          redirect_to payments_purchase_orders_path
          return
        end
      end
    end

    # Search for a purchase_order by purchase_payment ext_transaction_id
    def search_by_transaction_id
      purchase_orders = PurchaseOrder.filter(
        ext_transaction_id: params[:ext_transaction_id]
      )

      case purchase_orders.try(:count)
      when nil, 0
        flash[:error] = "Unable to find purchase order with the payments Ext Transaction ID: #{params[:ext_transaction_id]}"
        return redirect_to payments_purchase_orders_path
      when 1
        return redirect_to payments_purchase_order_path(purchase_orders.first.id)
      else
        @purchase_orders = purchase_orders
      end
    end

    # Search by origin_id - could be a Subscription (recurring) or Purchase Order (nonrecurring)
    def search_by_origin_id
      purchase_orders = PurchaseOrder.filter(origin_id: params[:origin_id])

      case purchase_orders.try(:count)
      when nil, 0
        flash[:error] = "Unable to find purchase with origin: #{params[:origin_id]}"
        return redirect_to payments_purchase_orders_path
      when 1
        return redirect_to payments_purchase_order_path(purchase_orders.first.id)
      else
        @purchase_orders = purchase_orders
      end
    end

    def search_by_purchase_order_id
      redirect_to payments_purchase_order_path(params[:purchase_order_id])
    end

    # rubocop:disable Metrics/CyclomaticComplexity
    # rubocop:disable Metrics/PerceivedComplexity
    def build_query_params(params)
      # rubocop:enable Metrics/CyclomaticComplexity
      # rubocop:enable Metrics/PerceivedComplexity
      query_params = params.symbolize_keys

      if params[:purchaser_id].present?
        user = Twitch::User.find(params[:purchaser_id])

        if user.blank?
          flash[:error] = "No user found with ID: #{params[:purchaser_id]}"
          return nil
        end

        params[:purchaser_login] = user.login
        query_params[:purchaser_id] = user.id
      end

      if params[:purchaser_login].present?
        user = Twitch::User.find_by_login(params[:purchaser_login])

        if user.blank?
          flash[:error] = "No user found with username: #{params[:purchaser_login]}"
          return nil
        end

        query_params[:purchaser_id] = user.id
      end

      if params[:recipient_login].present?
        user = Twitch::User.find_by_login(params[:recipient_login])

        if user.blank?
          flash[:error] = "No user found with username: #{params[:recipient_login]}"
          return nil
        end

        query_params[:recipient_id] = user.id
      end

      if params[:product_short_name].present?
        products = Subscriptions::TicketProduct.search(
          short_name: params[:product_short_name],
          exact_match: true
        )

        if products.blank?
          flash[:error] = "No product found with short name: #{params[:product_short_name]}"
          return nil
        end

        query_params[:product_id] = products.map(&:id)
      end

      if params[:product_channel_login].present?
        products = find_products_by_login(params)
        if products.nil?
          return nil
        end

        query_params[:product_id] = products.map(&:id)
      end

      if params[:product_type].present?
        if params[:product_type].to_sym == :gift_sub
          query_params[:gift] = true
          query_params[:product_type] = :sub
        end
      end

      return query_params
    end

    def search_purchase_orders
      query_params = build_query_params(params)
      if query_params.nil?
        return redirect_to(payments_purchase_orders_path)
      end

      unless valid_search_params(query_params)
        return redirect_to(payments_purchase_orders_path)
      end

      @purchase_orders = PurchaseOrder.filter(query_params)
      if @purchase_orders.nil?
        flash[:error] = "An error occurred searching for Purchase Orders. Please try again."
        redirect_to(payments_purchase_orders_path) unless performed?
        return
      end

      load_associations
    end

    def find_products_by_login(params)
      user = Twitch::User.find_by_login(params[:product_channel_login])

      if user.blank?
        flash[:error] = "No user found with login: #{params[:product_channel_login]}"
        return nil
      end

      products = Subscriptions::TicketProduct.search(
        channel_id: user.id,
        exact_match: true
      )

      if products.blank?
        flash[:error] = "No product found for login: #{params[:product_channel_login]}"
        return nil
      end

      return products
    end

    # TODO: Payments team needs to quantify what permutation of query
    # paremeters we should support - https://jira.twitch.com/browse/PAY-5065
    #
    ## Adding restrictions on params that cause joins on line items
    def valid_search_params(query_params)
      sanitized_params = PurchaseOrder.sanitize_filter_params(query_params)
      if sanitized_params.key?(:product_id) && sanitized_params.values.count == 1
        flash[:error] = "Please provide a Product Type and Benefit Recipient"
        return false
      elsif sanitized_params.key?(:recipient_id) && sanitized_params.values.count == 1
        flash[:error] = "Please provide a Product Type and Product Shortname/Channel Name"
        return false
      elsif sanitized_params.key?(:product_id) && sanitized_params.key?(:recipient_id) && sanitized_params.values.count == 2
        flash[:error] = "Please provide a Product Type"
        return false
      end

      return true
    end

    def email_purchase_orders_report(params)
      Payments::EmailPurchaseOrdersReport.do(params.merge({
                                                             admin_id: current_user.ldap_login,
                                                             admin_email: current_user.email,
                                                             full_details: true,
                                                             per_page: 5000,
                                                           }).reject { |_, v| v.blank? })
    end

    def to_csv
      CSV.generate do |csv|
        csv << PurchaseOrder::CSV_HEADERS.values
        @purchase_orders.each do |order|
          csv << order.to_csv_row
        end
      end
    end

    private

    def load_associations
      jobs = []
      @purchase_orders.each do |purchase_order|
        jobs += purchase_order.load_associations
      end

      jobs.map { |job| job.value(5) } # will block for a max of 5 seconds per job
    end

    def track_purchase_orders_search!(params:)
      track_action! trackable_request.assemble_an.admin_panel_purchase_orders_search(
        purchaser_login:      params[:purchaser_login],
        recipient_login:      params[:recipient_login],
        ext_subscription_id:  params[:ext_subscription_id],
        product_short_name:   params[:product_short_name],
        product_owner_login:  params[:product_channel_login],
        product_type:         params[:product_type],
        purchase_platform:    params[:platform],
        payment_provider:     params[:payment_gateway],
        purchase_order_state: params[:state],
        created_after_utc:    params[:created_at_start],
        created_before_utc:   params[:created_at_end],
        ext_transaction_id:   params[:ext_transaction_id],
        purchase_order_id:    params[:purchase_order_id],
        origin_id:            params[:origin_id]
      )
      return
    end
  end
end
