require 'spec_helper'

module Spree
  describe Api::AddressBooksController, type: :request do
    let!(:state) { create(:state) }
    let!(:harry_address_attributes) do
      {
        'firstname' => 'Harry',
        'lastname' => 'Potter',
        'address1' => '4 Privet Drive',
        'address2' => 'cupboard under the stairs',
        'city' => 'Surrey',
        'zipcode' => '10010',
        'phone' => '555-5555',
        'state_id' => state.id,
        'country_id' => state.country.id
      }
    end

    let!(:ron_address_attributes) do
      {
        'firstname' => 'Ron',
        'lastname' => 'Weasly',
        'address1' => 'Ottery St. Catchpole',
        'address2' => '4th floor',
        'city' => 'Devon, West Country',
        'zipcode' => '10010',
        'phone' => '555-5555',
        'state_id' => state.id,
        'country_id' => state.country.id
      }
    end

    context 'as address book owner' do
      context 'with ability' do
        it 'returns my address book' do
          user = create(:user, spree_api_key: 'galleon')
          user.save_in_address_book(harry_address_attributes, true)
          user.save_in_address_book(ron_address_attributes, false)

          get "/api/users/#{user.id}/address_book", nil, { 'X-SPREE-TOKEN' => 'galleon' }

          json_response = JSON.parse(response.body)
          expect(response.status).to eq(200)
          expect(json_response.length).to eq(2)
          expect(json_response).to include(
            hash_including(harry_address_attributes.merge!('default' => true)),
              hash_including(ron_address_attributes.merge!('default' => false))
            )
        end

        it 'updates my address book' do
          user = create(:user, spree_api_key: 'galleon')
          address = user.save_in_address_book(harry_address_attributes, true)
          harry_address_attributes['firstname'] = 'Ron'

          expect {
            put "/api/users/#{user.id}/address_book", { address_book: harry_address_attributes.merge('id' => address.id) }, { 'X-SPREE-TOKEN' => 'galleon' }
          }.to change { UserAddress.count }.from(1).to(2)

          expect(response.status).to eq(200)
          expect(JSON.parse(response.body).first).to include(harry_address_attributes)
        end

        context 'when creating an address' do
          it 'marks the update_target' do
            user = create(:user, spree_api_key: 'galleon')

            expect {
              put "/api/users/#{user.id}/address_book", { address_book: harry_address_attributes }, { 'X-SPREE-TOKEN' => 'galleon' }
            }.to change { UserAddress.count }.by(1)

            user_address = UserAddress.last

            expect(response.status).to eq(200)
            update_target_ids = JSON.parse(response.body).select { |a| a['update_target'] }.map { |a| a['id'] }
            expect(update_target_ids).to eq([user_address.address_id])
          end
        end

        context 'when updating an address' do
          it 'marks the update_target' do
            user = create(:user, spree_api_key: 'galleon')
            address = user.save_in_address_book(harry_address_attributes, true)

            expect {
              put "/api/users/#{user.id}/address_book", { address_book: harry_address_attributes }, { 'X-SPREE-TOKEN' => 'galleon' }
            }.to_not change { UserAddress.count }

            expect(response.status).to eq(200)
            update_target_ids = JSON.parse(response.body).select { |a| a['update_target'] }.map { |a| a['id'] }
            expect(update_target_ids).to eq([address.id])
          end
        end

        it 'archives my address' do
          address = create(:address)
          user = create(:user, spree_api_key: 'galleon')
          user.save_in_address_book(address.attributes, false)

          expect {
            delete "/api/users/#{user.id}/address_book", { address_id: address.id }, { 'X-SPREE-TOKEN' => 'galleon' }
          }.to change { user.reload.user_addresses.count }.from(1).to(0)

          expect(response.status).to eq(200)
        end
      end
    end

    context 'on behalf of address book owner' do
      context 'with ability' do
        before do
          Spree::RoleConfiguration.configure do |config|
            config.assign_permissions 'Prefect', [Spree::PermissionSets::UserManagement]
          end
          create(:user, spree_api_key: 'galleon', spree_roles: [build(:role, name: 'Prefect')])
        end

        it "returns another user's address book" do
          other_user = create(:user)
          other_user.save_in_address_book(harry_address_attributes, true)
          other_user.save_in_address_book(ron_address_attributes, false)

          get "/api/users/#{other_user.id}/address_book", nil, { 'X-SPREE-TOKEN' => 'galleon' }

          json_response = JSON.parse(response.body)
          expect(response.status).to eq(200)
          expect(json_response.length).to eq(2)
          expect(json_response).to include(
            hash_including(harry_address_attributes.merge!('default' => true)),
              hash_including(ron_address_attributes.merge!('default' => false))
            )
        end

        it "updates another user's address" do
          other_user = create(:user)
          address = other_user.save_in_address_book(harry_address_attributes, true)
          updated_harry_address = harry_address_attributes.merge('firstname' => 'Ron')

          expect {
            put "/api/users/#{other_user.id}/address_book", { address_book: updated_harry_address.merge('id' => address.id) }, { 'X-SPREE-TOKEN' => 'galleon' }
          }.to change { UserAddress.count }.from(1).to(2)

          expect(response.status).to eq(200)
          expect(JSON.parse(response.body).first).to include(updated_harry_address)
        end

        it "archives another user's address" do
          address = create(:address)
          other_user = create(:user)
          other_user.save_in_address_book(address.attributes, false)

          expect {
            delete "/api/users/#{other_user.id}/address_book", { address_id: address.id }, { 'X-SPREE-TOKEN' => 'galleon' }
          }.to change { other_user.reload.user_addresses.count }.from(1).to(0)

          expect(response.status).to eq(200)
        end
      end

      context 'without ability' do
        it 'does not return another user address book' do
          create(:user, spree_api_key: 'galleon')
          other_user = create(:user)
          other_user.save_in_address_book(harry_address_attributes, true)

          get "/api/users/#{other_user.id}/address_book", nil, { 'X-SPREE-TOKEN' => 'galleon' }

          expect(response.status).to eq(401)
        end

        it 'does not update another user address' do
          address = create(:address)
          other_user = create(:user)
          other_user_address = other_user.save_in_address_book(address.attributes, true)
          create(:user, spree_api_key: 'galleon')

          expect {
            put "/api/users/#{other_user.id}/address_book", { address_book: other_user_address.attributes.merge('address1' => 'Hogwarts') }, { 'X-SPREE-TOKEN' => 'galleon' }
          }.not_to change { UserAddress.count }

          expect(response.status).to eq(401)
        end

        it 'does not archive another user address' do
          address = create(:address)
          other_user = create(:user)
          other_user.save_in_address_book(address.attributes, true)
          create(:user, spree_api_key: 'galleon')

          expect {
            delete "/api/users/#{other_user.id}/address_book", { address_id: address.id }, { 'X-SPREE-TOKEN' => 'galleon' }
          }.not_to change { other_user.user_addresses.count }

          expect(response.status).to eq(401)
        end
      end
    end

    context 'unauthenticated' do
      before do
        @user = create(:user)
      end

      it 'GET returns a 401' do
        get "/api/users/#{@user.id}/address_book"
        expect(response.status).to eq(401)
      end

      it 'UPDATE returns a 401' do
        put "/api/users/#{@user.id}/address_book"
        expect(response.status).to eq(401)
      end

      it 'DELETE returns a 401' do
        delete "/api/users/#{@user.id}/address_book"
        expect(response.status).to eq(401)
      end
    end
  end
end