require 'spec_helper'
require 'ostruct'

describe Chronological::BaseStrategy do
  let(:strategy)    { Chronological::BaseStrategy.new(field_names)  }
  let(:chrono)      { OpenStruct.new(fields)                        }
  let(:fields)      { nil                                           }
  let(:field_names) do
    { starting_time: :start_time,
      ending_time:   :end_time }
  end

  describe '#starting_date' do
    context 'when working with a date/time' do
      let(:fields)  { { start_time:     Time.utc(2012, 7, 26, 7, 12, 0) } }

      it 'converts it to a date only' do
        expect(strategy.starting_date(chrono)).to eql Time.utc(2012, 7, 26, 7, 12, 0).to_date
      end
    end

    context 'when working with a string' do
      let(:fields) { { start_time:      '2012-07-26 03:15:12' } }

      it 'properly converts the date' do
        expect(strategy.starting_date(chrono)).to eql Time.utc(2012, 7, 26, 3, 15, 12).to_date
      end
    end

    context 'when working with nothing' do
      let(:fields) { { start_time:      nil } }

      it 'is nil' do
        expect(strategy.starting_date(chrono)).to be_nil
      end
    end
  end

  describe '#ending_date' do
    context 'when working with a date/time' do
      let(:fields)  { { end_time:     Time.utc(2012, 7, 26, 7, 12, 0) } }

      it 'converts it to a date only' do
        expect(strategy.ending_date(chrono)).to eql Time.utc(2012, 7, 26, 7, 12, 0).to_date
      end
    end

    context 'when working with a string' do
      let(:fields) { { end_time:      '2012-07-26 03:15:12' } }

      it 'properly converts the date' do
        expect(strategy.ending_date(chrono)).to eql Time.utc(2012, 7, 26, 3, 15, 12).to_date
      end
    end

    context 'when working with nothing' do
      let(:fields) { { end_time:      nil } }

      it 'is nil' do
        expect(strategy.ending_date(chrono)).to be_nil
      end
    end
  end

  describe '#inactive?' do
    it 'is the opposite of in_progress?' do
      allow(chrono).to receive(:in_progress?).and_return false

      expect(strategy.inactive?(chrono)).to eql true
    end
  end

  describe '#duration' do
    context 'when passed an options hash limiting the parts of the output' do
      let(:duration_hash) { strategy.duration(chrono, in: %i{days hours seconds}) }

      before { allow(strategy).to receive(:duration_in_seconds).and_return(438_263) }

      it 'removes it from the resulting hash' do
        expect(duration_hash).not_to have_key :minutes
      end

      it 'makes up for it in the following more granular item' do
        expect(duration_hash[:days]).to eql 5
        expect(duration_hash[:hours]).to eql 1
        expect(duration_hash[:seconds]).to eql 2663
      end
    end

    context 'when the strategy represents something with a duration' do
      before { allow(strategy).to receive(:duration_in_seconds).and_return(438_263) }

      it 'is a hash with the correct days' do
        expect(strategy.duration(chrono)[:days]).to eql 5
      end

      it 'is a hash with the correct hours' do
        expect(strategy.duration(chrono)[:hours]).to eql 1
      end

      it 'is a hash with the correct minutes' do
        expect(strategy.duration(chrono)[:minutes]).to eql 44
      end

      it 'is a hash with the correct seconds' do
        expect(strategy.duration(chrono)[:seconds]).to eql 23
      end
    end

    context 'when the strategy represents something with an even second time duration' do
      before { allow(strategy).to receive(:duration_in_seconds).and_return(6240) }

      it 'is a hash with the correct hours' do
        expect(strategy.duration(chrono)[:hours]).to eql 1
      end

      it 'is a hash with the correct minutes' do
        expect(strategy.duration(chrono)[:minutes]).to eql 44
      end

      it 'is a hash with the correct seconds' do
        expect(strategy.duration(chrono)[:seconds]).to eql 0
      end
    end

    context 'when the strategy represents something with an even minute time duration' do
      before { allow(strategy).to receive(:duration_in_seconds).and_return(3600) }

      it 'is a hash with the correct hours' do
        expect(strategy.duration(chrono)[:hours]).to eql 1
      end

      it 'is a hash with the correct minutes' do
        expect(strategy.duration(chrono)[:minutes]).to eql 0
      end

      it 'is a hash with the correct seconds' do
        expect(strategy.duration(chrono)[:seconds]).to eql 0
      end
    end

    context 'when the strategy represents something with a zero duration' do
      before { allow(strategy).to receive(:duration_in_seconds).and_return(0) }

      it 'is a hash with the correct hours' do
        expect(strategy.duration(chrono)[:hours]).to eql 0
      end

      it 'is a hash with the correct minutes' do
        expect(strategy.duration(chrono)[:minutes]).to eql 0
      end

      it 'is a hash with the correct seconds' do
        expect(strategy.duration(chrono)[:seconds]).to eql 0
      end
    end

    context 'when duration in seconds returns an empty value' do
      before { allow(strategy).to receive(:duration_in_seconds).and_return(nil) }

      it 'is an empty hash' do
        expect(strategy.duration(chrono)).to eql({})
      end
    end
  end

  describe '#in_progress?' do
    let(:later) { Time.local(2012, 7, 26, 6, 0, 26) }
    let(:now)   { Time.local(2012, 7, 26, 6, 0, 25) }
    let(:past)  { Time.local(2012, 7, 26, 6, 0, 24) }

    before      { Timecop.freeze(now)             }

    context 'when it does not have an absolute timeframe' do
      before { allow(strategy).to receive(:has_absolute_timeframe?).and_return(false) }

      it 'is false' do
        expect(strategy.in_progress?(chrono)).not_to eql true
      end
    end

    context 'when it does have an absolute timeframe' do
      before { allow(strategy).to receive(:has_absolute_timeframe?).and_return(true) }

      context 'and it has already started' do
        context 'and already ended' do
          let(:fields) do
            { start_time: past,
              end_time:   past }
          end

          it 'is false' do
            expect(strategy.in_progress?(chrono)).not_to eql true
          end
        end

        context 'and ends now' do
          let(:fields) do
            { start_time: past,
              end_time:   now }
          end

          it 'is false' do
            expect(strategy.in_progress?(chrono)).not_to eql true
          end
        end

        context 'and ends later' do
          let(:fields) do
            { start_time: past,
              end_time:   later }
          end

          it 'is true' do
            expect(strategy.in_progress?(chrono)).to eql true
          end
        end
      end

      context 'and there is a strategy that starts now' do
        context 'and ends now' do
          let(:fields) do
            { start_time: now,
              end_time:   now }
          end

          it 'is false' do
            expect(strategy.in_progress?(chrono)).not_to eql true
          end
        end

        context 'and ends later' do
          let(:fields) do
            { start_time: now,
              end_time:   later }
          end

          it 'is true' do
            expect(strategy.in_progress?(chrono)).to eql true
          end
        end
      end

      context 'and there is a strategy that has not yet started' do
        let(:fields) do
          { start_time: later,
            end_time:   later }
        end

        it 'is false' do
          expect(strategy.in_progress?(chrono)).not_to eql true
        end
      end
    end
  end

  describe '#scheduled_time_zone' do
    context 'when the time zone option is passed in' do
      let(:field_names) { { time_zone: :time_zone } }

      context 'and a time zone exists' do
        let(:fields) { { time_zone: 'Alaska' } }

        it 'is the time zone' do
          expect(strategy.send(:scheduled_time_zone, chrono, true)).to eql 'Alaska'
        end
      end

      context 'and a time zone does not exist' do
        let(:fields) { { time_zone: nil } }

        it 'is the time zone' do
          expect(strategy.send(:scheduled_time_zone, chrono, true)).to be_nil
        end
      end
    end

    context 'when the time zone option is not passed in' do
      let(:field_names) { Hash.new }

      it 'is equal to the default' do
        expect(strategy.send(:scheduled_time_zone, chrono, 'blasphemy!')).to eql 'blasphemy!'
      end
    end
  end
end
