# frozen_string_literal: true

require "system_helper"

RSpec.describe "As a consumer, I want to checkout my order" do
  include ShopWorkflow
  include CheckoutHelper
  include FileHelper
  include StripeHelper
  include StripeStubs
  include PaypalHelper
  include AuthenticationHelper
  include UIComponentHelper

  let!(:zone) { create(:zone_with_member) }
  let(:supplier) { create(:supplier_enterprise) }
  let(:distributor) { create(:distributor_enterprise, charges_sales_tax: true) }
  let(:product) {
    create(:taxed_product, supplier_id: supplier.id, price: 10, zone:, tax_rate_amount: 0.1)
  }
  let(:variant) { product.variants.first }
  let!(:order_cycle) {
    create(:simple_order_cycle, suppliers: [supplier], distributors: [distributor],
                                coordinator: create(:distributor_enterprise), variants: [variant])
  }
  let(:order) {
    create(:order_ready_for_confirmation, distributor:)
  }

  let(:fee_tax_rate) { create(:tax_rate, amount: 0.10, zone:, included_in_price: true) }
  let(:fee_tax_category) { create(:tax_category, tax_rates: [fee_tax_rate]) }
  let(:enterprise_fee) { create(:enterprise_fee, amount: 1.23, tax_category: fee_tax_category) }

  let(:free_shipping_with_required_address) {
    create(:shipping_method, require_ship_address: true,
                             name: "A Free Shipping with required address")
  }

  before do
    add_enterprise_fee enterprise_fee
    pick_order order

    distributor.shipping_methods.push(free_shipping_with_required_address)
  end

  context "as a logged in user" do
    let(:user) { create(:user) }

    before do
      login_as(user)
    end

    context "summary step" do
      describe "display the delivery address and not the ship address" do
        let(:ship_address) { create(:address, :randomized) }
        let(:bill_address) { create(:address, :randomized) }

        before do
          order.update_attribute(:ship_address, ship_address)
          order.update_attribute(:bill_address, bill_address)
          visit checkout_step_path(:summary)
        end

        it "displays title and ship address" do
          expect(page).to have_title "Checkout Summary - Open Food Network"

          expect(page).to have_content "Delivery address"
          expect(page).to have_content order.ship_address.address1
          expect(page).to have_content order.ship_address.city
          expect(page).to have_content order.ship_address.zipcode
          expect(page).to have_content order.ship_address.phone

          # but not the billing address
          expect(page).not_to have_content order.bill_address.address1
          expect(page).not_to have_content order.bill_address.city
          expect(page).not_to have_content order.bill_address.zipcode
          expect(page).not_to have_content order.bill_address.phone
        end
      end

      describe "with an order with special instructions" do
        before do
          order.update_attribute(:special_instructions, "Please deliver on Tuesday")
          visit checkout_step_path(:summary)
        end

        it "displays the special instructions" do
          expect(page).to have_content "Please deliver on Tuesday"
        end
      end

      describe "completing the checkout" do
        it "keeps the distributor selected for the current user after completion" do
          visit checkout_step_path(:summary)

          expect(page).to have_content "Shopping @ #{distributor.name}"

          place_order

          expect(page).to have_content "Back To Store"
          expect(order.reload.state).to eq "complete"

          expect(page).to have_content "Shopping @ #{distributor.name}"
        end
      end

      describe "navigating away from checkout summary page" do
        it "navigates to new page when popup is confirmed" do
          visit checkout_step_path(:summary)
          expect(page).to have_content "Order summary"
          within '.nav-main-menu' do
            accept_alert do
              click_link(href: '/groups')
            end
          end
          expect(page).not_to have_content "Order summary"
          expect(page).to have_content "Groups / regions"
        end

        it "doesn't navigate to new page when popup is canceled" do
          visit checkout_step_path(:summary)
          expect(page).to have_content "Order summary"
          within '.nav-main-menu' do
            dismiss_confirm do
              click_link(href: '/groups')
            end
          end
          expect(page).to have_content "Order summary"
          expect(page).not_to have_content "Groups / regions"
        end

        it "opens correct order step when edit link is clicked" do
          visit checkout_step_path(:summary)
          expect(page).to have_content "Order summary"
          click_link(href: '/checkout/details')

          expect(page).to have_content "Contact information"
          expect(page).not_to have_content "Groups / regions"
        end
      end

      describe "navigation available" do
        it "redirect to Payment method step by clicking on 'Payment method' link" do
          visit checkout_step_path(:summary)

          click_link "Payment method"

          expect(page).to have_content("You can review and confirm your order in the next step " \
                                       "which includes the final costs.")
        end
      end

      describe "terms and conditions" do
        let(:customer) { create(:customer, enterprise: order.distributor, user:) }
        let(:tos_url) { "https://example.org/tos" }
        let(:system_terms) { terms_pdf_file }
        let(:shop_terms) { terms_pdf_file }

        context "when none are required" do
          it "doesn't show checkbox or links" do
            visit checkout_step_path(:summary)

            within "#checkout" do
              expect(page).not_to have_field "order_accept_terms"
              expect(page).not_to have_link "Terms and Conditions"
              expect(page).not_to have_link "Terms of service"
            end
          end
        end

        context "when distributor has T&Cs" do
          before do
            order.distributor.update!(terms_and_conditions: shop_terms)
          end

          describe "when customer has not accepted T&Cs before" do
            it "shows a link to the T&Cs and disables checkout button until terms are accepted" do
              visit checkout_step_path(:summary)
              expect(page).to have_link "Terms and Conditions", href: /Terms-of-service\.pdf/
              expect(page).to have_field "order_accept_terms", checked: false
            end
          end

          describe "when customer has already accepted T&Cs before" do
            before do
              customer.update terms_and_conditions_accepted_at: Time.zone.now
            end

            it "enables checkout button (because T&Cs are accepted by default)" do
              visit checkout_step_path(:summary)
              expect(page).to have_field "order_accept_terms", checked: true
            end

            describe "but afterwards the enterprise has uploaded a new T&Cs file" do
              before { order.distributor.update!(terms_and_conditions: shop_terms) }

              it "disables checkout button until terms are accepted" do
                visit checkout_step_path(:summary)
                expect(page).to have_field "order_accept_terms", checked: false
              end
            end
          end
        end

        context "when the platform's terms of service have to be accepted" do
          before do
            allow(Spree::Config).to receive(:shoppers_require_tos).and_return(true)
          end

          let!(:tos) do
            TermsOfServiceFile.create!(attachment: system_terms)
          end

          it "shows the terms which need to be accepted" do
            visit checkout_step_path(:summary)

            expect(page).to have_link("Terms of service", href: /Terms-of-service.pdf/, count: 2)
            expect(find_link("Terms of service")[:target]).to eq "_blank"
            expect(page).to have_field "order_accept_terms", checked: false
          end

          context "when the terms have been accepted in the past" do
            context "with a dedicated ToS file" do
              before do
                tos.update!(
                  updated_at: 1.day.ago
                )
                customer.update(terms_and_conditions_accepted_at: Time.zone.now)
              end

              it "remembers the selection" do
                visit checkout_step_path(:summary)

                expect(page).to have_link("Terms of service", href: /Terms-of-service.pdf/)
                expect(page).to have_field "order_accept_terms", checked: true
              end
            end

            context "with the default ToS file" do
              before do
                customer.update(terms_and_conditions_accepted_at: Time.zone.now)
              end

              it "remembers the selection" do
                pending "#10675"

                visit checkout_step_path(:summary)

                expect(page).to have_link("Terms of service", href: tos_url)
                expect(page).to have_field "order_accept_terms", checked: true
              end
            end
          end
        end

        context "when the seller's terms and the platform's terms have to be accepted" do
          before do
            order.distributor.update!(terms_and_conditions: shop_terms)

            allow(Spree::Config).to receive(:shoppers_require_tos).and_return(true)
          end

          let!(:tos) do
            TermsOfServiceFile.create!(attachment: system_terms)
          end

          it "shows links to both terms and all need accepting" do
            visit checkout_step_path(:summary)

            expect(page).to have_link "Terms and Conditions", href: /Terms-of-service\.pdf/
            expect(page).to have_link("Terms of service", href: /Terms-of-service\.pdf/, count: 2)
            expect(page).to have_field "order_accept_terms", checked: false
          end
        end
      end

      context "handle the navigation when the order is ready for confirmation" do
        it "redirect to summary step" do
          visit "/checkout"

          expect(page).to have_current_path checkout_step_path(:summary)
        end

        it "handle the navigation between each step by clicking tabs/buttons to submit the form" do
          visit checkout_step_path(:summary)

          click_on "Your details"

          expect(page).to have_current_path checkout_step_path(:details)

          click_on "Next - Payment method"

          expect(page).to have_current_path checkout_step_path(:payment)
        end
      end

      describe "order state" do
        before do
          visit checkout_step_path(:summary)
        end

        it "emptying the cart changes the order state back to address" do
          visit main_app.cart_path
          expect {
            find('#clear_cart_link').click
            expect(page).to have_current_path enterprise_shop_path(distributor)
          }.to change { order.reload.state }.from("confirmation").to("address")
        end
      end

      describe "vouchers" do
        let(:voucher) do
          create(:voucher_flat_rate, code: 'some_code', enterprise: distributor, amount: 6)
        end

        before do
          add_voucher_to_order(voucher, order)
        end

        it "shows the applied voucher" do
          visit checkout_step_path(:summary)

          within ".summary-right" do
            expect(page).to have_content "some_code"
            expect(page).to have_content "-6"
          end
        end

        context "with voucher deactivated after being added to an order" do
          it "completes the order" do
            visit checkout_step_path(:summary)

            # Deactivate voucher
            voucher.destroy

            place_order

            expect(order.reload.state).to eq "complete"
          end
        end
      end

      context "with a VINE voucher", feature: :connected_apps do
        let!(:vine_connected_app) {
          ConnectedApps::Vine.create(
            enterprise: distributor, data: { api_key: "1234568", secret: "my_secret" }
          )
        }
        let(:vine_voucher) {
          create(:vine_voucher, code: 'some_vine_code', enterprise: distributor, amount: 0.01)
        }

        before do
          allow(ENV).to receive(:fetch).and_call_original
          allow(ENV).to receive(:fetch).with("VINE_API_URL")
            .and_return("https://vine-staging.openfoodnetwork.org.au/api/v1")

          add_voucher_to_order(vine_voucher, order)
        end

        it "shows the applied voucher" do
          visit checkout_step_path(:summary)

          within ".summary-right" do
            expect(page).to have_content "some_vine_code"
            expect(page).to have_content "-0.01"
          end
        end

        context "when placing the order" do
          it "completes the order", :vcr do
            visit checkout_step_path(:summary)

            place_order

            within "#line-items" do
              expect(page).to have_content "Voucher: some_vine_code"
              expect(page).to have_content "$-0.01"
            end
            expect(order.reload.state).to eq "complete"
          end
        end
      end

      context "with customer credit" do
        let(:credit_payment_method) { create(:customer_credit_payment_method) }
        let(:order) { create(:order_ready_for_payment, distributor:) }
        let(:payment_amount) { 10.00 }

        before do
          create(:payment_method, distributors: [distributor])
          create(
            :customer_account_transaction,
            amount: 100,
            customer: order.customer,
          )
          # Add credit payment
          payment = order.payments.create!(payment_method: credit_payment_method,
                                           amount: payment_amount)
          payment.internal_purchase!
        end

        it "displays the customer credit used" do
          # Move to ready for confirmation
          order.next!

          visit checkout_step_path(:summary)

          expect(page).to have_content "Customer credit"

          within ".summary-right" do
            expect(page).to have_content "Credit"
            expect(page).to have_selector("#customer-credit", text: with_currency(-10.00))
            expect(page).to have_selector("#order_total", text: with_currency(0.00))
          end
        end

        context "when completing order" do
          it "displays the order as paid" do
            # Move to ready for confirmation
            order.next!

            visit checkout_step_path(:summary)
            place_order

            expect(page).to have_content "PAID"
            expect(page).to have_content "Paying via: Customer credit"
            expect(page).to have_selector("#customer-credit", text: with_currency(-10.00))
            expect(page).to have_selector("#amount-paid", text: with_currency(0.00))
          end
        end

        context "when credit doesn't cover the whole order" do
          let(:payment_amount) { 2.00 }
          let(:payment_method) { create(:payment_method, distributors: [distributor]) }

          before do
            # Add another payment to cover the rest of the order
            order.payments.create!(payment_method:, amount: 8.00)
          end

          it "displays the customer credit used" do
            # Move to ready for confirmation
            order.next!

            visit checkout_step_path(:summary)

            expect(page).to have_content payment_method.display_name

            within ".summary-right" do
              expect(page).to have_content "Credit"
              expect(page).to have_selector("#customer-credit", text: with_currency(-2.00))
              # actual order total is 10.00
              expect(page).to have_selector("#order_total", text: with_currency(8.00))
            end

            last_payment = order.payments.last
            expect(last_payment.amount).to eq(8.00) # it is the balance due
          end

          context "when completing order" do
            it "displays part of the order that was paid with credit" do
              # Move to ready for confirmation
              order.next!

              visit checkout_step_path(:summary)
              place_order

              expect(page).to have_content "NOT PAID"
              expect(page).to have_content "Paying via: #{payment_method.display_name}"
              within "#line-items" do
                expect(page).to have_selector("#customer-credit", text: with_currency(-2.00))
                expect(page).to have_selector("#amount-paid", text: with_currency(0.00))
                expect(page).to have_content("Balance Due")
                # actual order total is 10.00
                expect(page).to have_selector("#balance-due", text: with_currency(8.00))
              end
            end
          end
        end
      end
    end

    context "with previous open orders" do
      let(:order) {
        create(:order_ready_for_confirmation, distributor:,
                                              order_cycle:, user_id: user.id)
      }
      let!(:prev_order) {
        create(:completed_order_with_totals,
               order_cycle:, distributor:, user_id: order.user_id)
      }

      context "when distributor allows order changes" do
        before do
          order.distributor.allow_order_changes = true
          order.distributor.save
          visit checkout_step_path(:summary)
        end

        it "informs about previous orders" do
          expect(page).to have_content("You have an order for this order cycle already.")
        end

        it "show a link to /cart#bought-products page" do
          expect(page).to have_link("cart", href: "/cart#bought-products")
          click_on "cart"
          expect(page).to have_text(
            "#{prev_order.line_items.length} " \
            "additional items already confirmed for this order cycle"
          )
        end
      end

      it "don't display any message if distributor don't allow order changes" do
        order.distributor.allow_order_changes = false
        order.distributor.save
        visit checkout_step_path(:summary)

        expect(page).not_to have_content("You have an order for this order cycle already.")
      end
    end

    describe "order confirmation page" do
      let(:completed_order) {
        create(:order_ready_to_ship, distributor:,
                                     order_cycle:, user_id: user.id)
      }
      let(:payment) { completed_order.payments.first }

      context "an order with balance due" do
        before { set_up_order(-10, "balance_due") }

        it "displays balance due and paid state" do
          expect(page).to have_selector('h5', text: "Balance Due")
          expect(page).to have_selector('h5', text: with_currency(10))

          confirmation_page_expect_paid(paid_state: "NOT PAID", paid_amount: 40)
        end
      end

      context "an order with credit owed" do
        before { set_up_order(10, "credit_owed") }

        it "displays Credit owned and paid state" do
          expect(page).to have_selector('h5', text: "Credit Owed")
          expect(page).to have_selector('h5', text: with_currency(-10))

          confirmation_page_expect_paid(paid_state: "PAID", paid_amount: 60)
        end
      end

      context "an order with no outstanding balance" do
        before { set_up_order(0, "paid") }

        it "displays paid state" do
          expect(page).not_to have_selector('h5', text: "Credit Owed")
          expect(page).not_to have_selector('h5', text: "Balance Due")

          confirmation_page_expect_paid(paid_state: "PAID", paid_amount: 50)
        end
      end
    end
  end

  def confirmation_page_expect_paid(paid_state:, paid_amount:)
    expect(page).to have_content paid_state.to_s
    expect(page).to have_selector('strong', text: "Amount Paid")
    expect(page).to have_selector('strong', text: with_currency(paid_amount))
  end

  def add_voucher_to_order(voucher, order)
    voucher.create_adjustment(voucher.code, order)
    OrderManagement::Order::Updater.new(order).update_voucher
  end

  def set_up_order(balance, state)
    payment.update!(amount: payment.amount + balance)
    completed_order.reload
    expect(completed_order.payment_state).to eq state.to_s
    visit "/orders/#{completed_order.number}"
  end
end
