require "day_time"

describe DayTime do
  describe "Kernel#DayTime" do
    it "proxies calls" do
      expect(DayTime("08:00")).to eq DayTime.new("08:00")
    end
  end

  describe "#initialize" do
    context "with other DayTime" do
      it "returns a new DayTime" do
        day_time = DayTime.new("08:00")
        expect(DayTime.new(day_time)).to eq day_time
      end
    end

    context "with seconds since midnight" do
      it "sets hours, minutes and seconds" do
        day_time = DayTime.new(20_066) # 05:34:25

        expect(day_time.hours).to eq 5
        expect(day_time.minutes).to eq 34
        expect(day_time.seconds).to eq 26

        expect(day_time.seconds_since_midnight).to be 20_066
      end

      it "sets hours, minutes and seconds" do
        day_time = DayTime.new(0) # 00:00:00

        expect(day_time.hours).to eq 0
        expect(day_time.minutes).to eq 0
        expect(day_time.seconds).to eq 0

        expect(day_time.seconds_since_midnight).to be 0
      end

      it "sets hours, minutes and seconds" do
        day_time = DayTime.new(86_399) # 23:59:59

        expect(day_time.hours).to eq 23
        expect(day_time.minutes).to eq 59
        expect(day_time.seconds).to eq 59

        expect(day_time.seconds_since_midnight).to be 86_399
      end

      it "sets hours, minutes and seconds" do
        day_time = DayTime.new(86_400) # 00:00:00

        expect(day_time.hours).to eq 0
        expect(day_time.minutes).to eq 0
        expect(day_time.seconds).to eq 0

        expect(day_time.seconds_since_midnight).to be 0
      end

      context "with overflowing seconds" do
        it "starts over at midnight" do
          day_time = DayTime.new(96_685) # 26:51:25 <-> 02:51:25

          expect(day_time.hours).to eq 2
          expect(day_time.minutes).to eq 51
          expect(day_time.seconds).to eq 25

          expect(day_time.seconds_since_midnight).to be 10_285
        end
      end
    end

    context "with hh:mm string" do
      it "sets hours and minutes" do
        day_time = DayTime.new("12:31")

        expect(day_time.hours).to be 12
        expect(day_time.minutes).to be 31
        expect(day_time.seconds).to be 0
      end
    end

    context "with hh:mm:ss string" do
      it "sets hours, minutes and seconds" do
        day_time = DayTime.new("12:31:22")

        expect(day_time.hours).to be 12
        expect(day_time.minutes).to be 31
        expect(day_time.seconds).to be 22
      end
    end

    context "with malformed string" do
      it "raises an ArgumentError" do
        expect {
          DayTime.new("Y2:X1")
        }.to raise_error(ArgumentError)
      end
    end

    context "with hours-minutes-keyed Hash" do
      it "sets hours and minutes" do
        day_time = DayTime.new(hours: 13, minutes: 55)

        expect(day_time.hours).to be 13
        expect(day_time.minutes).to be 55
        expect(day_time.seconds).to be 00
      end
    end

    context "with hours-minutes-seconds-keyed Hash" do
      it "sets hours, minutes and seconds" do
        day_time = DayTime.new(hours: 13, minutes: 55, seconds: 45)

        expect(day_time.hours).to be 13
        expect(day_time.minutes).to be 55
        expect(day_time.seconds).to be 45
      end
    end

    context "with Time" do
      it "sets hours, minutes and seconds" do
        time = Time.new(2020, 10, 10, 18, 30, 45) # 18:30:45
        day_time = DayTime.new(time)

        expect(day_time.hours).to be 18
        expect(day_time.minutes).to be 30
        expect(day_time.seconds).to be 45
      end
    end

    context "with DateTime" do
      it "sets hours, minutes and seconds" do
        date_time = DateTime.new(2020, 10, 10, 18, 30, 45, "+2") # 18:30:45
        day_time = DayTime.new(date_time)

        expect(day_time.hours).to be 18
        expect(day_time.minutes).to be 30
        expect(day_time.seconds).to be 45
      end
    end
  end

  describe "#==" do
    it "compares by seconds since midnight" do
      a = DayTime.new("08:00")
      b = DayTime.new("08:00")

      expect(a == b).to be true
    end
  end

  describe "#seconds_since_midnight" do
    it "returns the seconds since midnight" do
      day_time = DayTime.new("04:22:44")
      expect(day_time.seconds_since_midnight).to be 15_764
    end
  end

  describe "#<=>" do
    let(:morning) { DayTime.new("08:00") }
    let(:noon)    { DayTime.new("12:00") }
    let(:evening) { DayTime.new("20:00") }

    it "compares by seconds since midnight" do
      expect(morning < evening).to be true
    end

    it "supports ranges" do
      expect(morning..evening).to include noon
    end
  end

  describe "#succ" do
    it "returns a DayTime advanced by a second" do
      expect(DayTime.new("08:00").succ).to eq DayTime.new("08:00:01")
    end

    context "with overflowing seconds" do
      it "starts over at midnight" do
        expect(DayTime.new("23:59:59").succ).to eq DayTime.new("00:00:00")
      end
    end
  end

  describe "#each" do
    it "returns DayTimes advanced by a second" do
      enum = DayTime.new("23:59:59").to_enum

      expect(enum.next).to eq DayTime.new("00:00:00")
      expect(enum.next).to eq DayTime.new("00:00:01")
    end
  end

  describe "#+" do
    let(:morning) { DayTime.new("08:00") }
    let(:noon)    { DayTime.new("12:00") }

    context "with other DayTime" do
      it "adds seconds since midnight" do
        expect(morning + noon).to eq DayTime.new("20:00")
      end
    end

    context "with other object" do
      it "adds seconds since midnight" do
        expect(morning + "00:00:15").to eq DayTime.new("08:00:15")
      end
    end
  end

  describe "#-" do
    let(:noon)    { DayTime.new("12:00") }
    let(:morning) { DayTime.new("08:00") }

    context "with other DayTime" do
      it "subtracts seconds since midnight" do
        expect(morning - noon).to eq DayTime.new("04:00")
      end
    end

    context "with other object" do
      it "adds seconds since midnight" do
        expect(morning - 15).to eq DayTime.new("07:59:45")
      end
    end
  end

  describe "#to_s" do
    it "returns a HH:MM:SS formatted string" do
      expect(DayTime.new("04:22:44").to_s).to eq "04:22:44"
    end

    context "with excluded seconds" do
      it "returns a HH:MM formatted string" do
        day_time = DayTime.new("04:22:44")
        expect(day_time.to_s(include_seconds: false)).to eq "04:22"
      end
    end
  end

  describe "#to_i" do
    it "returns the seconds since midnight" do
      day_time = DayTime.new("05:34:25")
      expect(day_time.to_i).to eq day_time.seconds_since_midnight
    end
  end
end
