# frozen_string_literal: true

require 'spec_helper'

RSpec.describe PaymentGateways::PaypalController do
  context '#cancel' do
    it 'redirects back to checkout' do
      expect(get(:cancel)).to redirect_to checkout_path
    end
  end

  context '#confirm' do
    let(:previous_order) { controller.current_order(true) }
    let(:payment_method) { create(:payment_method) }

    before do
      allow(previous_order).to receive(:complete?).and_return(true)
      allow(previous_order).to receive(:checkout_allowed?).and_return(true)
    end

    it 'resets the order' do
      post :confirm, params: { payment_method_id: payment_method.id }
      expect(controller.current_order).not_to eq(previous_order)
    end

    it 'sets the access token of the session' do
      post :confirm, params: { payment_method_id: payment_method.id }
      expect(session[:access_token]).to eq(controller.current_order.token)
    end

    context "if the stock ran out whilst the payment was being placed" do
      it "redirects to the details page with out of stock error" do
        mock_order_check_stock_service(controller.current_order)

        post(:confirm, params: { payment_method_id: payment_method.id })

        expect(response).to redirect_to checkout_step_path(step: :details)

        order = controller.current_order.reload

        # Order is in "cart" state and did not complete processing of the payment
        expect(order.state).to eq "cart"
        expect(order.payments.count).to eq 0
      end
    end

    context "when order completion fails" do
      before do
        allow(previous_order).to receive(:complete?).and_return(false)
      end

      it "redirects to checkout state path" do
        expect(post(:confirm, params: { payment_method_id: payment_method.id })).
          to redirect_to checkout_step_path(step: :payment)

        expect(flash[:error]).to eq(
          'Payment could not be processed, please check the details you entered'
        )
      end
    end
  end

  describe "#express" do
    let(:order) { create(:order_with_totals_and_distribution) }
    let(:response) { true }
    let(:provider_success_url) { "https://test.com/success" }
    let(:response_mock) { double(:response, success?: response, errors: [] ) }
    let(:provider_mock) {
      double(:provider, build_set_express_checkout: true,
                        set_express_checkout: response_mock,
                        express_checkout_url: provider_success_url)
    }

    before do
      allow(controller).to receive(:current_order) { order }
      allow(controller).to receive(:provider) { provider_mock }
      allow(controller).to receive(:express_checkout_request_details) { {} }
    end

    context "when processing is successful" do
      it "redirects to a success URL generated by the payment provider" do
        expect(post(:express)).to redirect_to provider_success_url
      end
    end

    context "when processing fails" do
      let(:response) { false }

      it "redirects to checkout_step_path with a flash error" do
        expect(post(:express)).to redirect_to checkout_step_path(:payment)
        expect(flash[:error]).to eq "PayPal failed. "
      end
    end

    context "when a SocketError is encountered during processing" do
      before do
        allow(response_mock).to receive(:success?).and_raise(SocketError)
      end

      it "redirects to checkout_step_path with a flash error" do
        expect(post(:express)).to redirect_to checkout_step_path(:payment)
        expect(flash[:error]).to eq "Could not connect to PayPal."
      end
    end

    context "when the stock ran out whilst the payment was being placed" do
      it "redirects to the details page with out of stock error" do
        mock_order_check_stock_service(controller.current_order)

        post(:express)

        expect(response).to redirect_to checkout_step_path(step: :details)
      end
    end
  end

  describe '#expire_current_order' do
    it 'empties the order_id of the session' do
      expect(session).to receive(:[]=).with(:order_id, nil)
      controller.__send__(:expire_current_order)
    end

    it 'resets the @current_order ivar' do
      controller.__send__(:expire_current_order)
      expect(controller.instance_variable_get(:@current_order)).to be_nil
    end
  end

  def mock_order_check_stock_service(order)
    check_stock_service_mock = instance_double(Orders::CheckStockService)
    expect(Orders::CheckStockService).to receive(:new).and_return(check_stock_service_mock)
    expect(check_stock_service_mock).to receive(:sufficient_stock?).and_return(false)
    expect(check_stock_service_mock).to receive(:update_line_items).and_return(order.variants)
  end
end
