# frozen_string_literal: true

require 'rails_helper'

RSpec.describe TopicTimer, type: :model do
  fab!(:topic_timer) { Fabricate(:topic_timer) }
  fab!(:topic) { Fabricate(:topic) }
  fab!(:admin) { Fabricate(:admin) }

  before { freeze_time }

  context "validations" do
    describe "pending_timers scope" do
      it "does not return deleted timers" do
        topic_timer.trash!
        expect(TopicTimer.pending_timers.pluck(:id)).not_to include(topic_timer.id)
      end

      it "does not return timers in the future of the provided before time" do
        topic_timer.update!(execute_at: 3.days.from_now)
        expect(TopicTimer.pending_timers.pluck(:id)).not_to include(topic_timer.id)
        expect(TopicTimer.pending_timers(2.days.from_now).pluck(:id)).not_to include(topic_timer.id)
        topic_timer.update!(execute_at: 1.minute.ago, created_at: 10.minutes.ago)
        expect(TopicTimer.pending_timers.pluck(:id)).to include(topic_timer.id)
      end

      describe "duration values" do
        it "does not allow durations <= 0" do
          topic_timer.duration_minutes = -1
          topic_timer.save
          expect(topic_timer.errors.full_messages.first).to include("Duration minutes must be greater than 0.")
        end

        it "does not allow crazy big durations (20 years in minutes)" do
          topic_timer.duration_minutes = 21.years.to_i / 60
          topic_timer.save
          expect(topic_timer.errors.full_messages.first).to include("Duration minutes cannot be more than 20 years.")
        end
      end
    end
    describe '#status_type' do
      it 'should ensure that only one active public topic status update exists' do
        topic_timer.update!(topic: topic)
        Fabricate(:topic_timer, deleted_at: Time.zone.now, topic: topic)

        expect { Fabricate(:topic_timer, topic: topic) }
          .to raise_error(ActiveRecord::RecordInvalid)
      end
    end

    describe '#execute_at' do
      describe 'when #execute_at is greater than #created_at' do
        it 'should be valid' do
          topic_timer = Fabricate.build(:topic_timer,
            execute_at: Time.zone.now + 1.hour,
            user: Fabricate(:user),
            topic: Fabricate(:topic)
          )

          expect(topic_timer).to be_valid
        end
      end

      describe 'when #execute_at is smaller than #created_at' do
        it 'should not be valid' do
          topic_timer = Fabricate.build(:topic_timer,
            execute_at: Time.zone.now - 1.hour,
            created_at: Time.zone.now,
            user: Fabricate(:user),
            topic: Fabricate(:topic)
          )

          expect(topic_timer).to_not be_valid
        end
      end
    end

    describe '#category_id' do
      describe 'when #status_type is publish_to_category' do
        describe 'when #category_id is not present' do
          it 'should not be valid' do
            topic_timer = Fabricate.build(:topic_timer,
              status_type: TopicTimer.types[:publish_to_category]
            )

            expect(topic_timer).to_not be_valid
            expect(topic_timer.errors).to include(:category_id)
          end
        end

        describe 'when #category_id is present' do
          it 'should be valid' do
            topic_timer = Fabricate.build(:topic_timer,
              status_type: TopicTimer.types[:publish_to_category],
              category_id: Fabricate(:category).id,
              user: Fabricate(:user),
              topic: Fabricate(:topic)
            )

            expect(topic_timer).to be_valid
          end
        end
      end
    end
  end

  context 'callbacks' do
    describe 'when #execute_at and #user_id are not changed' do
      it 'should not schedule another to update topic' do
        Jobs.expects(:enqueue_at).never

        topic_timer.update!(topic: Fabricate(:topic))
      end
    end

    describe 'when a open topic status update is created for an open topic' do
      fab!(:topic) { Fabricate(:topic, closed: false) }
      fab!(:topic_timer) do
        Fabricate(:topic_timer,
          status_type: described_class.types[:open],
          topic: topic
        )
      end

      before do
        Jobs.run_immediately!
      end

      it 'should close the topic' do
        topic_timer.send(:schedule_auto_open_job)
        expect(topic.reload.closed).to eq(true)
      end
    end

    describe 'when a close topic status update is created for a closed topic' do
      fab!(:topic) { Fabricate(:topic, closed: true) }
      fab!(:topic_timer) do
        Fabricate(:topic_timer,
          status_type: described_class.types[:close],
          topic: topic
        )
      end

      before do
        Jobs.run_immediately!
      end

      it 'should open the topic' do
        topic_timer.send(:schedule_auto_close_job)
        expect(topic.reload.closed).to eq(false)
      end
    end

    describe '#public_type' do
      [:close, :open, :delete].each do |public_type|
        it "is true for #{public_type}" do
          timer = Fabricate(:topic_timer, status_type: described_class.types[public_type])
          expect(timer.public_type).to eq(true)
        end
      end

      it "is true for publish_to_category" do
        timer = Fabricate(:topic_timer, status_type: described_class.types[:publish_to_category], category: Fabricate(:category))
        expect(timer.public_type).to eq(true)
      end

      described_class.private_types.keys.each do |private_type|
        it "is false for #{private_type}" do
          timer = Fabricate(:topic_timer, status_type: described_class.types[private_type])
          expect(timer.public_type).to be_falsey
        end
      end
    end
  end

  describe "runnable?" do
    it "returns false if execute_at > now" do
      topic_timer = Fabricate.build(:topic_timer,
                                    execute_at: Time.zone.now + 1.hour,
                                    user: Fabricate(:user),
                                    topic: Fabricate(:topic)
                                   )

      expect(topic_timer.runnable?).to eq(false)
    end

    it "returns false if timer is deleted" do
      topic_timer = Fabricate.create(:topic_timer,
                                    execute_at: Time.zone.now - 1.hour,
                                    created_at: Time.zone.now - 2.hour,
                                    user: Fabricate(:user),
                                    topic: Fabricate(:topic)
                                   )
      topic_timer.trash!

      expect(topic_timer.runnable?).to eq(false)
    end

    it "returns true if execute_at < now" do
      topic_timer = Fabricate.build(:topic_timer,
                                    execute_at: Time.zone.now - 1.hour,
                                    created_at: Time.zone.now - 2.hour,
                                    user: Fabricate(:user),
                                    topic: Fabricate(:topic)
                                   )

      expect(topic_timer.runnable?).to eq(true)
    end
  end
end