# frozen_string_literal: true

require_relative 'shared_context_for_backup_restore'

RSpec.describe BackupRestore::SystemInterface do
  include_context "with shared stuff"

  subject { BackupRestore::SystemInterface.new(logger) }

  describe "readonly mode" do
    after do
      Discourse::READONLY_KEYS.each { |key| Discourse.redis.del(key) }
    end

    describe "#enable_readonly_mode" do
      it "enables readonly mode" do
        Discourse.expects(:enable_readonly_mode).once
        subject.enable_readonly_mode
      end

      it "does not enable readonly mode when it is already in readonly mode" do
        Discourse.enable_readonly_mode
        Discourse.expects(:enable_readonly_mode).never
        subject.enable_readonly_mode
      end
    end

    describe "#disable_readonly_mode" do
      it "disables readonly mode" do
        Discourse.expects(:disable_readonly_mode).once
        subject.disable_readonly_mode
      end

      it "does not disable readonly mode when readonly mode was explicitly enabled" do
        Discourse.enable_readonly_mode
        Discourse.expects(:disable_readonly_mode).never
        subject.disable_readonly_mode
      end
    end
  end

  describe "#mark_restore_as_running" do
    it "calls mark_restore_as_running" do
      BackupRestore.expects(:mark_as_running!).once
      subject.mark_restore_as_running
    end
  end

  describe "#mark_restore_as_not_running" do
    it "calls mark_restore_as_not_running" do
      BackupRestore.expects(:mark_as_not_running!).once
      subject.mark_restore_as_not_running
    end
  end

  describe "#listen_for_shutdown_signal" do
    before { BackupRestore.mark_as_running! }

    after do
      BackupRestore.clear_shutdown_signal!
      BackupRestore.mark_as_not_running!
    end

    it "exits the process when shutdown signal is set" do
      expect do
        thread = subject.listen_for_shutdown_signal
        BackupRestore.set_shutdown_signal!
        thread.join
      end.to raise_error(SystemExit)
    end

    it "clears an existing shutdown signal before it starts to listen" do
      BackupRestore.set_shutdown_signal!
      expect(BackupRestore.should_shutdown?).to eq(true)

      thread = subject.listen_for_shutdown_signal
      expect(BackupRestore.should_shutdown?).to eq(false)
      Thread.kill(thread)
    end
  end

  describe "#pause_sidekiq" do
    after { Sidekiq.unpause! }

    it "calls pause!" do
      expect(Sidekiq.paused?).to eq(false)
      subject.pause_sidekiq("my reason")
      expect(Sidekiq.paused?).to eq(true)
      expect(Discourse.redis.get(SidekiqPauser::PAUSED_KEY)).to eq("my reason")
    end
  end

  describe "#unpause_sidekiq" do
    it "calls unpause!" do
      Sidekiq.pause!
      expect(Sidekiq.paused?).to eq(true)

      subject.unpause_sidekiq
      expect(Sidekiq.paused?).to eq(false)
    end
  end

  describe "#wait_for_sidekiq" do
    it "waits 6 seconds even when there are no running Sidekiq jobs" do
      subject.expects(:sleep).with(6).once
      subject.wait_for_sidekiq
    end

    context "with Sidekiq workers" do
      after do
        flush_sidekiq_redis_namespace
      end

      def flush_sidekiq_redis_namespace
        Sidekiq.redis do |redis|
          redis.scan_each { |key| redis.del(key) }
        end
      end

      def create_workers(site_id: nil, all_sites: false)
        payload = Sidekiq::Testing.fake! do
          data = { post_id: 1 }

          if all_sites
            data[:all_sites] = true
          else
            data[:current_site_id] = site_id || RailsMultisite::ConnectionManagement.current_db
          end

          Jobs.enqueue(:process_post, data)
          Jobs::ProcessPost.jobs.last
        end

        Sidekiq.redis do |conn|
          hostname = "localhost"
          pid = 7890
          key = "#{hostname}:#{pid}"
          process = { pid: pid, hostname: hostname }

          conn.sadd('processes', key)
          conn.hmset(key, 'info', Sidekiq.dump_json(process))

          data = Sidekiq.dump_json(
            queue: 'default',
            run_at: Time.now.to_i,
            payload: Sidekiq.dump_json(payload)
          )
          conn.hmset("#{key}:work", '444', data)
        end
      end

      it "waits up to 60 seconds for jobs running for the current site to finish" do
        subject.expects(:sleep).with(6).times(10)
        create_workers
        expect { subject.wait_for_sidekiq }.to raise_error(BackupRestore::RunningSidekiqJobsError)
      end

      it "waits up to 60 seconds for jobs running on all sites to finish" do
        subject.expects(:sleep).with(6).times(10)
        create_workers(all_sites: true)
        expect { subject.wait_for_sidekiq }.to raise_error(BackupRestore::RunningSidekiqJobsError)
      end

      it "ignores jobs of other sites" do
        subject.expects(:sleep).with(6).once
        create_workers(site_id: "another_site")

        subject.wait_for_sidekiq
      end
    end
  end

  describe "#flush_redis" do
    context "with Sidekiq" do
      after { Sidekiq.unpause! }

      it "doesn't unpause Sidekiq" do
        Sidekiq.pause!
        subject.flush_redis

        expect(Sidekiq.paused?).to eq(true)
      end
    end
  end
end