require 'rails_helper'

RSpec.describe Payments::PurchaseOrder do
  describe "#filter" do
    subject { Payments::PurchaseOrder.filter(params) }
    let(:params) { {} }

    describe "with no params" do
      it "returns []" do
        expect(subject).to eq([])
      end
    end

    describe "with only invalid params" do
      let(:params) { { some: "invalid", parameters: "here" } }
      it "returns []" do
        expect(subject).to eq([])
      end
    end

    describe "with valid params" do
      let(:params) { { purchaser_id: 12345 } }
      let(:response) { double(:response, success?: true, body: body) }
      let(:body) { { "purchase_orders" => purchase_orders } }
      let(:purchase_orders) { [] }

      before do
        allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders", any_args).and_return(response)
        allow(Payments::PurchaseOrder).to receive(:from_attributes).and_return(Payments::PurchaseOrder.new)
      end

      describe "with no matching purchase orders" do
        it "should return []" do
          expect(subject).to eq([])
        end
      end

      describe "with matching purchase orders" do
        let(:purchase_orders) { [{ id: 12345, purchaser_id: 1 }, { id: 12346, purchaser_id: 2 }] }
        it "should return the results" do
          expect(subject).to satisfy("right number of purchase orders") { |subj| subj.count == purchase_orders.count }
        end

        it "should return purchase order objects" do
          expect(subject).to satisfy("purchase order objects") { |subj| subj.all? { |po| po.class == Payments::PurchaseOrder } }
        end
      end

      describe "with payments service call failure" do
        let(:response) { double(:response, success?: false) }
        it "should return []" do
          expect(subject).to eq(nil)
        end
      end
    end
  end

  describe "#find" do
    describe "with no id" do
      it "returns nil" do
        expect(Payments::PurchaseOrder.find(nil)).to be nil
      end
    end

    describe "with nonexistent id" do
      it "returns nil if that purchase order doesn't exist" do
        resp = double(:response, success?: false)
        allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/kappa").and_return(resp)
        purchase_order = Payments::PurchaseOrder.find("kappa")
        expect(purchase_order).to be nil
      end
    end

    describe "with a valid id" do
      let(:id) { 1234 }
      let(:recipient_id) { 1 }

      it "returns a purchase order with appropriate fields" do
        purchase_order = {
            id: id
        }
        purchase_order_payments = double(:purchase_order_payments)
        purchase_order_line_items = double(:purchase_order_line_items)
        subscription = double(:subscription)
        item_fulfillments = [{
                                 recipient_id: recipient_id
                             }]
        refund_reasons = [{ refund_reason: "HelpPortalRefund" }]

        resp = double(:response,
                      {
                          body: {
                              "purchase_order" => purchase_order,
                              "purchase_order_payments" => purchase_order_payments,
                              "purchase_order_line_items" => purchase_order_line_items,
                              "subscription" => subscription,
                              "item_fulfillments" => item_fulfillments,
                              "refund_reasons" => refund_reasons
                          },
                          success?: true
                      })
        allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{id}").and_return(resp)
        allow(Twitch::User).to receive(:find).with(recipient_id).and_return(double(:user))
        order = Payments::PurchaseOrder.find(id)

        expect(order.id).to eq(id)
        expect(order.purchase_order_payments).to eq(purchase_order_payments)
        expect(order.purchase_order_line_items).to eq(purchase_order_line_items)
        expect(order.subscription).to eq(subscription)
        expect(order.item_fulfillments).to eq(item_fulfillments)
        expect(order.refund_reasons).to eq(refund_reasons)

      end
    end
  end

  describe "#count_for_purchaser" do
    let(:user_id) { 1234 }
    let(:count) { 17 }

    it "should return the count on success" do
      resp = double(:response,
                    {
                        body: {
                            "count" => count
                        },
                        success?: true
                    })
      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/count", params: { user_id: user_id }).and_return(resp)
      c = Payments::PurchaseOrder.count_for_purchaser(user_id)

      expect(c).to eq(count)
    end

    it "should return 0 on failure" do
      resp = double(:response,
                    {
                        body: {
                        },
                        success?: false
                    })
      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/count", params: { user_id: user_id }).and_return(resp)
      c = Payments::PurchaseOrder.count_for_purchaser(user_id)

      expect(c).to eq(0)
    end
  end

  describe "#product" do
    subject { purchase_order.product }

    describe "when ticket product not found" do
      let(:product_id) { 5432 }
      let(:purchase_order) { Payments::PurchaseOrder.from_attributes(id: 1234, product_id: product_id) }
      before do
        allow(Subscriptions::TicketProduct).to receive(:find).with(product_id).and_return(Subscriptions::TicketProduct.from_errors("some error"))
      end

      it "should have nil product" do
        expect(subject).to be_nil
      end
    end
  end

  describe "refund_fraud" do
    let(:admin_id) { 1234 }
    let(:order_id) { 17 }

    it "should return success when success" do
      resp = double(:response,
                    {
                        success?: true
                    })
      allow(Payments::PurchaseOrder).to receive(:post).with("/admin/refund_orders", body: { order_ids: [order_id], reason: 'fraud', admin_id: admin_id }).and_return(resp)
      c = Payments::PurchaseOrder.refund_as_fraud(order_id, admin_id)

      expect(c).to be true
    end

    it "should return false on failure" do
      resp = double(:response,
                    {
                        success?: false
                    })
      allow(Payments::PurchaseOrder).to receive(:post).with("/admin/refund_orders", body: { order_ids: [order_id], reason: 'fraud', admin_id: admin_id }).and_return(resp)
      c = Payments::PurchaseOrder.refund_as_fraud(order_id, admin_id)

      expect(c).to be false
    end
  end
  describe "mark_refunded_by_finance" do
    let(:admin_id) { 1234 }
    let(:order_id) { 17 }

    it "should return success when success" do
      resp = double(:response,
                    {
                        success?: true
                    })
      allow(Payments::PurchaseOrder).to receive(:put).with("/admin_panel/purchase_orders/#{order_id}/mark_refunded_by_finance", body: { order_ids: [order_id], admin_id: admin_id }).and_return(resp)
      c = Payments::PurchaseOrder.mark_refunded_by_finance(order_id, admin_id)

      expect(c).to be true
    end
    it "should return failure when failure" do
      resp = double(:response,
                    {
                        success?: false
                    })
      allow(Payments::PurchaseOrder).to receive(:put).with("/admin_panel/purchase_orders/#{order_id}/mark_refunded_by_finance", body: { order_ids: [order_id], admin_id: admin_id }).and_return(resp)
      c = Payments::PurchaseOrder.mark_refunded_by_finance(order_id, admin_id)

      expect(c).to be false
    end
  end

  describe "label_fraud" do
    let(:admin_id) { 1234 }
    let(:order_id) { 17 }

    it "should return success when success" do
      resp = double(:response,
                    {
                        success?: true
                    })
      allow(Payments::PurchaseOrder).to receive(:post).with("/admin_panel/label", body: { entity_id: order_id, entity_type: 'purchase_order', label: 'fraud', label_type: 'fraud', admin_id: admin_id }).and_return(resp)
      c = Payments::PurchaseOrder.label_fraud(order_id, 'fraud', admin_id)

      expect(c).to be true
    end

    it "should return false on failure" do
      resp = double(:response,
                    {
                        success?: false
                    })
      allow(Payments::PurchaseOrder).to receive(:post).with("/admin_panel/label", body: { entity_id: order_id, entity_type: 'purchase_order', label: 'fraud', label_type: 'fraud', admin_id: admin_id }).and_return(resp)
      c = Payments::PurchaseOrder.label_fraud(order_id, 'fraud', admin_id)

      expect(c).to be false
    end
  end

  describe "fraud" do
    let(:order_id) { 13 }
    let(:id) { 1234 }
    let(:origin_id) { "asdf" }
    let(:gross_amount) { 5 }

    it "should return success when success" do
      purchase_order = double(:response,
                              {
                                  success?: true,
                                  body: {
                                      "purchase_order" => {
                                          id: id,
                                          origin_id: origin_id,
                                          gross_amount: gross_amount
                                      }
                                  }
                              })
      everdeen_params = {
          body: {
              origin_id: origin_id,
              amount: gross_amount,
              refund_reason: "admin panel refund"
          }.to_json,
          headers: {
              "content-type" => "application/json"
          }
      }
      resp = double(:response,
                    {
                        success?: true
                    })

      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{order_id}").and_return(purchase_order)
      allow(Payments::PurchaseOrder).to receive(:post).with("#{Settings.everdeen.endpoint}/twirp/code.justin.tv.revenue.everdeen.Everdeen/RefundOrder", everdeen_params).and_return(resp)
      c = Payments::PurchaseOrder.refund(order_id, gross_amount)

      expect(c).to be true
    end

    it "should return False when Refund QTY Is not same as gross_amount" do
      purchase_order = double(:response,
                              {
                                  success?: true,
                                  body: {
                                      "purchase_order" => {
                                          id: id,
                                          origin_id: origin_id,
                                          gross_amount: gross_amount
                                      }
                                  }
                              })
      everdeen_params = {
          body: {
              origin_id: origin_id,
              amount: gross_amount,
              refund_reason: "admin panel refund"
          }.to_json,
          headers: {
              "content-type" => "application/json"
          }
      }
      resp = double(:response,
                    {
                        success?: true
                    })

      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{order_id}").and_return(purchase_order)
      allow(Payments::PurchaseOrder).to receive(:post).with("#{Settings.everdeen.endpoint}/twirp/code.justin.tv.revenue.everdeen.Everdeen/RefundOrder", everdeen_params).and_return(resp)
      c = Payments::PurchaseOrder.refund(order_id, 10)

      expect(c).to be false
    end

    it "should send retain_benefits when refunding a gift sub bought with sub tokens" do
      purchase_order = double(:response,
                              {
                                  success?: true,
                                  body: {
                                      "purchase_order" => {
                                          id: id,
                                          origin_id: origin_id,
                                          gross_amount: gross_amount,
                                          is_gift: true,
                                          currency: "sub_token"
                                      }
                                  }
                              })
      everdeen_params = {
          body: {
              origin_id: origin_id,
              amount: gross_amount,
              refund_reason: "admin panel refund",
              benefits_directive: :RETAIN_BENEFITS
          }.to_json,
          headers: {
              "content-type" => "application/json"
          }
      }
      resp = double(:response,
                    {
                        success?: true
                    })

      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{order_id}").and_return(purchase_order)
      allow(Payments::PurchaseOrder).to receive(:post).with("#{Settings.everdeen.endpoint}/twirp/code.justin.tv.revenue.everdeen.Everdeen/RefundOrder", everdeen_params).and_return(resp)
      c = Payments::PurchaseOrder.refund(order_id, gross_amount)

      expect(c).to be true
    end

    it "should return false if no order found" do
      purchase_order = double(:response,
                              {
                                  success?: false
                              })

      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{order_id}").and_return(purchase_order)
      c = Payments::PurchaseOrder.refund(order_id, gross_amount)

      expect(c).to be false
    end

    it "should return false on refund failure" do
      purchase_order = double(:response,
                              {
                                  success?: true,
                                  body: {
                                      "purchase_order" => {
                                          id: id,
                                          origin_id: origin_id,
                                          gross_amount: gross_amount
                                      }
                                  }
                              })
      resp = double(:response,
                    {
                        success?: false
                    })
      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{order_id}").and_return(purchase_order)
      allow(Payments::PurchaseOrder).to receive(:post).with("#{Settings.everdeen.endpoint}/twirp/code.justin.tv.revenue.everdeen.Everdeen/RefundOrder", anything).and_return(resp)
      c = Payments::PurchaseOrder.refund(order_id, gross_amount)

      expect(c).to be false
    end

    it "should return false on If qty is Invalid <= 0 || > gross_amount" do
      purchase_order = double(:response,
                              {
                                  success?: true,
                                  body: {
                                      "purchase_order" => {
                                          id: id,
                                          origin_id: origin_id,
                                          gross_amount: gross_amount
                                      }
                                  }
                              })
      resp = double(:response,
                    {
                        success?: false
                    })
      allow(Payments::PurchaseOrder).to receive(:get).with("/admin_panel/purchase_orders/#{order_id}").and_return(purchase_order)
      allow(Payments::PurchaseOrder).to receive(:post).with("#{Settings.everdeen.endpoint}/twirp/code.justin.tv.revenue.everdeen.Everdeen/RefundOrder", anything).and_return(resp)
      c = Payments::PurchaseOrder.refund(order_id, 0) # QTY <= 0
      expect(c).to be false
      c = Payments::PurchaseOrder.refund(order_id, 10) # QTY > Gross_Amount
      expect(c).to be false
    end

  end
end
