# frozen_string_literal: true

require 'spec_helper'

module Calagator
  describe Event::Search, type: :model do
    subject(:event_search) { described_class.new(search_params) }

    let(:events) { double }
    before { allow(Event).to receive(:search).and_return(events) }

    before { allow(Event).to receive(:search_tag).and_return(events) }

    let(:search_params) { {} }

    describe 'by keyword' do
      let(:search_params) { { query: 'myquery' } }

      it 'finds all events matching the keyword, ordered by date' do
        event_search.events
        expect(Event).to have_received(:search).with('myquery', skip_old: false, order: 'date')
      end

      context 'limited to current events' do
        let(:search_params) { { query: 'myquery', current: '1' } }

        it 'is able to only return current events' do
          event_search.events
          expect(Event).to have_received(:search).with('myquery', order: 'date', skip_old: true)
        end
      end

      context 'with an invalid order' do
        let(:search_params) { { query: 'myquery', order: 'kittens' } }

        it 'sets a failure message as a warning' do
          expect(event_search.failure_message).to eq('Unknown ordering option "kittens", sorting by date instead.')
        end

        it 'is not a hard failure' do
          expect(event_search).not_to be_hard_failure
        end
      end

      context 'when the search encounters an error' do
        before { allow(Event).to receive(:search).and_raise(ActiveRecord::StatementInvalid, 'bad times') }

        before { event_search.events }

        it 'sets a failure message' do
          expect(event_search.failure_message).to eq('There was an error completing your search.')
        end

        it 'is a hard failure' do
          expect(event_search).to be_hard_failure
        end

        it 'returns no events' do
          expect(event_search.events).to be_empty
        end
      end
    end

    describe 'by tag' do
      let(:search_params) { { tag: 'foo' } }

      it 'finds all events matching the tag, ordered by date' do
        event_search.events
        expect(Event).to have_received(:search_tag).with('foo', current: false, order: 'date')
      end

      context 'with an invalid order' do
        let(:search_params) { { tag: 'omg', order: 'kittens' } }

        it 'sets a failure message as a warning' do
          expect(event_search.failure_message).to eq('Unknown ordering option "kittens", sorting by date instead.')
        end

        it 'is not a hard failure' do
          expect(subject).not_to be_hard_failure
        end
      end

      context 'attempting to order by score' do
        let(:search_params) { { tag: 'omg', order: 'score' } }

        it 'sets a failure message as a warning' do
          expect(event_search.failure_message).to eq('You cannot sort tags by score')
        end

        it 'is not a hard failure' do
          expect(event_search).not_to be_hard_failure
        end
      end

      context 'when the tag search encounters an error' do
        before { allow(Event).to receive(:search_tag).and_raise(ActiveRecord::StatementInvalid.new('bad times')) }

        before { event_search.events }

        it 'sets a failure message' do
          expect(event_search.failure_message).to eq('There was an error completing your search.')
        end

        it 'is a hard failure' do
          expect(event_search).to be_hard_failure
        end

        it 'returns no events' do
          expect(event_search.events).to be_empty
        end
      end
    end

    describe '#grouped_events' do
      let(:past_event) { double(:event, current?: false) }
      let(:current_event) { double(:event, current?: true) }
      let(:events) { [past_event, current_event] }
      let(:search_params) { { query: 'ruby' } }

      it 'groups events into a hash by currentness' do
        expect(event_search.grouped_events).to eq(
          past: [past_event],
          current: [current_event]
        )
      end

      context "when passed the 'current' option" do
        let(:search_params) { { query: 'ruby', current: 'true' } }

        it 'discards past events' do
          expect(event_search.grouped_events).to eq(
            past: [],
            current: [current_event]
          )
        end
      end

      context "when passing 'date' to the order option" do
        let(:search_params) { { query: 'ruby', order: 'date' } }

        let(:other_past_event) { double(:event, current?: false) }
        let(:events) { [current_event, past_event, other_past_event] }

        it 'orders past events by date desc' do
          expect(event_search.grouped_events).to eq(
            current: [current_event],
            past: [past_event, other_past_event]
          )
        end
      end
    end

    describe 'hard failures' do
      context 'when given neither search query nor tag' do
        let(:search_params) { {} }

        it 'sets a failure message' do
          expect(event_search.failure_message).to eq('You must enter a search query')
        end

        it 'is a hard failure' do
          expect(event_search).to be_hard_failure
        end

        it 'returns no events' do
          expect(event_search.events).to be_empty
        end
      end

      context 'when given both search query and tag' do
        let(:search_params) { { query: 'omg', tag: 'bbq' } }

        it 'sets a failure message' do
          expect(event_search.failure_message).to eq("You can't search by tag and query at the same time")
        end

        it 'is a hard failure' do
          expect(event_search).to be_hard_failure
        end

        it 'returns no events' do
          expect(event_search.events).to be_empty
        end
      end
    end
  end
end
