# 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

  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, order_cycle:, distributor:, bill_address_id: nil,
                   ship_address_id: nil, state: "cart",
                   line_items: [create(:line_item, variant:)])
  }

  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")
  }
  let(:free_shipping) {
    create(:shipping_method, require_ship_address: false, name: "free Shipping",
                             description: "yellow",
                             calculator: Calculator::FlatRate.new(preferred_amount: 0.00))
  }
  let(:shipping_tax_rate) { create(:tax_rate, amount: 0.25, zone:, included_in_price: true) }
  let(:shipping_tax_category) { create(:tax_category, tax_rates: [shipping_tax_rate]) }
  let(:shipping_with_fee) {
    create(:shipping_method, require_ship_address: true, tax_category: shipping_tax_category,
                             name: "Shipping with Fee", description: "blue",
                             calculator: Calculator::FlatRate.new(preferred_amount: 4.56))
  }
  let(:free_shipping_without_required_address) {
    create(:shipping_method, require_ship_address: false,
                             name: "Z Free Shipping without required address")
  }

  let!(:payment_with_fee) {
    create(:payment_method, distributors: [distributor],
                            name: "Payment with Fee", description: "Payment with fee",
                            calculator: Calculator::FlatRate.new(preferred_amount: 1.23))
  }
  let(:shipping_backoffice_only) {
    create(:shipping_method, require_ship_address: true, name: "Shipping Backoffice Only",
                             display_on: "back_end")
  }
  let(:shipping_methods) {
    [free_shipping_with_required_address, free_shipping, shipping_with_fee,
     free_shipping_without_required_address]
  }

  before do
    add_enterprise_fee enterprise_fee
    pick_order order

    distributor.shipping_methods.push(shipping_methods)
  end

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

    before do
      login_as(user)
      visit checkout_path
    end

    context "when no selecting a shipping method" do
      before do
        fill_out_details
        fill_out_billing_address
      end

      it "errors is shown to the user when submitting the form" do
        click_button "Next - Payment method"
        expect(page).to have_content "Select a shipping method"
      end
    end

    context "details step" do
      context "when form is submitted but invalid" do
        it "display the checkbox about shipping address same as billing address " \
           "when selecting a shipping method that requires ship address" do
          choose free_shipping_with_required_address.name
          check "Shipping address same as billing address?"
          expect(page).to have_content "Save as default shipping address"

          click_button "Next - Payment method"

          expect(page).to have_content "Saving failed, please update the highlighted fields."
          expect(page).to have_content "Save as default shipping address"
          expect(page).to have_checked_field "Shipping address same as billing address?"
        end
      end

      describe "filling out order details" do
        before do
          fill_out_details
          fill_out_billing_address
          order.update(user_id: user.id)
        end

        context "billing address" do
          describe "checking the default address box" do
            before do
              check "order_save_bill_address"
              choose free_shipping.name
            end

            it "creates a new default bill address" do
              expect {
                proceed_to_payment
                order.reload
                user.reload
              }.to change {
                order.bill_address&.address1
              }.from(nil).to("Rue de la Vie, 77")
                .and change {
                       order.customer&.bill_address&.address1
                     }.from(nil).to("Rue de la Vie, 77")
                .and change {
                       order.bill_address&.address1
                     }
                .from(nil).to("Rue de la Vie, 77")
            end
          end

          describe "unchecking the default address box" do
            before do
              uncheck "order_save_bill_address"
              choose free_shipping.name
            end

            context "proceeding to payment" do
              before do
                expect {
                  proceed_to_payment
                }.not_to change {
                  user.reload.bill_address
                }
              end

              it "updates the bill address of the order and customer" do
                expect(order.reload.bill_address.address1).to eq "Rue de la Vie, 77"
                expect(order.customer.bill_address.address1).to eq "Rue de la Vie, 77"
              end
            end
          end
        end

        context "shipping address" do
          describe "checking the default address box" do
            before do
              choose free_shipping_with_required_address.name
              uncheck "ship_address_same_as_billing"
              fill_out_shipping_address
              check "order_save_ship_address"
            end

            context "proceeding to payment" do
              it "creates a new default ship address" do
                expect {
                  proceed_to_payment
                  order.reload
                  user.reload
                }.to change {
                  order.ship_address&.address1
                }.from(nil).to("Rue de la Vie, 66")
                  .and change {
                         order.customer&.ship_address&.address1
                       }.from(nil).to("Rue de la Vie, 66")
                  .and change {
                         order.ship_address&.address1
                       }
                  .from(nil).to("Rue de la Vie, 66")
              end
            end
          end

          describe "unchecking the default address box" do
            before do
              choose free_shipping_with_required_address.name
              uncheck "ship_address_same_as_billing"
              fill_out_shipping_address
              uncheck "order_save_ship_address"
            end

            context "proceeding to payment" do
              before do
                expect {
                  proceed_to_payment
                }.not_to change {
                           user.reload.ship_address
                         }
              end

              it "updates the ship address of the order and customer" do
                expect(order.reload.ship_address.address1).to eq "Rue de la Vie, 66"
                expect(order.customer.ship_address.address1).to eq "Rue de la Vie, 66"
              end
            end
          end
        end

        describe "selecting a pick-up shipping method and submiting the form" do
          before do
            choose free_shipping.name
          end

          it "redirects the user to the Payment Method step" do
            fill_notes("SpEcIaL NoTeS")
            proceed_to_payment
          end

          context 'when the user has no shipping address' do
            before do
              # Hack so we can have "Shipping address same as billing address?" unticked
              choose free_shipping_with_required_address.name
              uncheck "Shipping address same as billing address?"
              choose free_shipping_without_required_address.name
            end

            it "redirects the user to the Payment Method step" do
              proceed_to_payment
            end
          end
        end

        describe "selecting a delivery method with a shipping fee" do
          before do
            choose shipping_with_fee.name
          end

          context "with same shipping and billing address" do
            before do
              check "ship_address_same_as_billing"
            end

            it "displays the shipping fee" do
              expect(page).to have_content("#{shipping_with_fee.name} " + with_currency(4.56).to_s)
            end

            it "does not display the shipping address form" do
              expect(page).not_to have_field "order_ship_address_attributes_address1"
            end

            it "redirects the user to the Payment Method step, when submiting the form" do
              proceed_to_payment
              # asserts whether shipping and billing addresses are the same
              ship_add_id = order.reload.ship_address_id
              bill_add_id = order.reload.bill_address_id
              expect(Spree::Address.where(id: bill_add_id).pluck(:address1) ==
                Spree::Address.where(id: ship_add_id).pluck(:address1)).to be true
            end

            context "with a shipping fee" do
              before do
                proceed_to_payment
                click_button "Next - Order summary"
              end

              shared_examples "displays the shipping fee" do |checkout_page|
                it "on the #{checkout_page} page" do
                  expect(page).to have_content("Shipping #{with_currency(4.56)}")

                  if checkout_page.eql?("order confirmation")
                    expect(page).to have_content "Your order has been processed successfully"
                  end
                end
              end

              it_behaves_like "displays the shipping fee", "order summary"

              context "after completing the order" do
                before do
                  click_on "Complete order"
                end
                it_behaves_like "displays the shipping fee", "order confirmation"
              end
            end
          end

          context "with different shipping and billing address" do
            before do
              uncheck "ship_address_same_as_billing"
            end
            it "displays the shipping address form and the option to save it as default" do
              expect(page).to have_field "order_ship_address_attributes_address1"
            end

            it "displays error messages when submitting incomplete billing address" do
              click_button "Next - Payment method"
              within "checkout" do
                expect(page).to have_field("Address", with: "")
                expect(page).to have_field("City", with: "")
                expect(page).to have_field("Postcode", with: "")
                expect(page).to have_content("can't be blank", count: 3)
              end
              within ".flash[type='error']" do
                expect(page).to have_content "Saving failed, please update the highlighted fields."
              end
            end

            it "fills in shipping details and redirects the user to the Payment Method step,
            when submiting the form" do
              fill_out_shipping_address
              fill_notes("SpEcIaL NoTeS")
              proceed_to_payment
              # asserts whether shipping and billing addresses are the same
              ship_add_id = Spree::Order.first.ship_address_id
              bill_add_id = Spree::Order.first.bill_address_id
              expect(Spree::Address.where(id: bill_add_id).pluck(:address1) ==
               Spree::Address.where(id: ship_add_id).pluck(:address1)).to be false
            end
          end
        end

        describe "pre-selecting a shipping method" do
          it "preselect a shipping method if only one is available" do
            order.distributor.update! shipping_methods: [free_shipping, shipping_backoffice_only]

            visit checkout_step_path(:details)

            expect(page).to have_checked_field "shipping_method_#{free_shipping.id}"
          end

          it "don't preselect a shipping method if more than one is available" do
            order.distributor.update! shipping_methods: [free_shipping, shipping_with_fee]

            visit checkout_step_path(:details)

            expect(page).to have_field "shipping_method_#{free_shipping.id}", checked: false
            expect(page).to have_field "shipping_method_#{shipping_with_fee.id}", checked: false
          end
        end

        context "wiht customer credit" do
          let!(:credit_payment_method) { create(:customer_credit_payment_method) }
          let(:credit_amount) { 100.00 }
          let(:customer) { create(:customer, user:, enterprise: distributor) }

          before do
            order.update(customer_id: customer.id)
            order.update_totals_and_states

            create(
              :customer_account_transaction,
              amount: credit_amount,
              customer: order.customer,
            )
            visit checkout_step_path(:details)
            fill_out_details
            fill_out_billing_address
            choose free_shipping.name
            proceed_to_payment
          end

          it "adds a customer credit payment" do
            credit_payment = order.payments.find_by(payment_method: credit_payment_method)
            expect(credit_payment).not_to be_nil
            expect(credit_payment.amount).to eq(10.00) # order.total is 10.00
          end

          context "when credit doesn't cover the whole order" do
            let(:credit_amount) { 5.00 }

            it "adds a customer credit payment using the remaining credit" do
              credit_payment = order.payments.find_by(payment_method: credit_payment_method)
              expect(credit_payment).not_to be_nil
              expect(credit_payment.amount).to eq(5.00) # order.total is 10.00
            end
          end
        end
      end

      describe "not filling out delivery details" do
        before do
          fill_in "Email", with: ""
        end
        it "should display error when fields are empty" do
          click_button "Next - Payment method"
          within "checkout" do
            expect(page).to have_field("First Name", with: "")
            expect(page).to have_field("Last Name", with: "")
            expect(page).to have_field("Email", with: "")
            expect(page).to have_content("is invalid")
            expect(page).to have_field("Phone number", with: "")
            expect(page).to have_field("Address", with: "")
            expect(page).to have_field("City", with: "")
            expect(page).to have_field("Postcode", with: "")
            expect(page).to have_content("can't be blank", count: 7)
            expect(page).to have_content("Select a shipping method")
          end
          within ".flash[type='error']" do
            expect(page).to have_content("Saving failed, please update the highlighted fields")
          end
        end
      end

      context "with a saved address" do
        let!(:address_state) do
          create(:state, name: "Testville", abbr: "TST", country: DefaultCountry.country )
        end
        let(:saved_address) do
          create(:bill_address, state: address_state, zipcode: "TST01" )
        end

        before do
          user.update_columns bill_address_id: saved_address.id
        end

        it "pre-fills address details" do
          # Check for the right title first. This is a random place here but
          # we don't have a standard success checkout flow case to add this to.
          expect(page).to have_title "Checkout Details - Open Food Network"

          visit checkout_path
          expect(page).to have_select(
            "order_bill_address_attributes_state_id", selected: "Testville"
          )
          expect(page).to have_field "order_bill_address_attributes_zipcode", with: "TST01"
        end
      end
    end
  end
end
