require 'rails_helper'

describe PendingAnalysis do
  describe '.poll' do
    it 'polls videos with pending video analysis' do
      allow(PendingAnalysis::PendingVideoAnalysis).to receive(:poll)

      PendingAnalysis.poll

      expect(PendingAnalysis::PendingVideoAnalysis).to have_received(:poll)
    end

    it 'polls videos with pending audio analysis' do
      allow(PendingAnalysis::PendingAudioAnalysis).to receive(:poll)

      PendingAnalysis.poll

      expect(PendingAnalysis::PendingAudioAnalysis).to have_received(:poll)
    end
  end

  describe '.expire' do
    let(:age_threshold) { 2.days.ago }

    it 'expires videos with pending video analysis' do
      allow(PendingAnalysis::PendingVideoAnalysis).to receive(:expire)

      PendingAnalysis.expire(age_threshold)

      expect(PendingAnalysis::PendingVideoAnalysis).to have_received(:expire).with(age_threshold)
    end

    it 'expires videos with pending audio analysis' do
      allow(PendingAnalysis::PendingAudioAnalysis).to receive(:expire)

      PendingAnalysis.expire(age_threshold)

      expect(PendingAnalysis::PendingAudioAnalysis).to have_received(:expire).with(age_threshold)
    end
  end

  describe PendingAnalysis::PendingVideoAnalysis do
    describe '.poll' do
      it 'polls all videos with pending analysis' do 
        pending_analysis = create(:video, analysis_status: :pending, analysis_uid: "pending_uid")
        not_pending_analysis = create(:video, analysis_status: :success, analysis_uid: "not_pending_uid")

        allow(described_class).to receive(:new).and_return(spy)

        described_class.poll

        expect(described_class).to have_received(:new).with("pending_uid")
        expect(described_class).not_to have_received(:new).with("not_pending_uid")
      end
    end

    describe '.expire' do
      it 'expires all videos with pending analysis older than a given time' do
        pending_analysis_inside_range = create(:video, 
                                               analysis_status: :pending, 
                                               analysis_uid: "pending_inside_uid", 
                                               analysis_started_at: 1.week.ago)
        pending_analysis_outside_range = create(:video, 
                                                analysis_status: :pending, 
                                                analysis_uid: "pending_outside_uid", 
                                                analysis_started_at: 1.minute.ago)
        not_pending_analysis = create(:video, 
                                      analysis_status: :success, 
                                      analysis_uid: "not_pending_uid")

        allow(described_class).to receive(:new).and_return(spy)

        described_class.expire(1.day.ago)

        expect(described_class).to have_received(:new).with("pending_inside_uid")
        expect(described_class).not_to have_received(:new).with("pending_outside_uid")
        expect(described_class).not_to have_received(:new).with("not_pending_uid")
      end
    end

    describe '#poll' do
      let(:video) { create(:video, analysis_status: :pending, analysis_uid: "analysis_uid") }

      subject { PendingAnalysis::PendingVideoAnalysis.new(video.analysis_uid) }

      context 'when API results are present' do
        it 'updates the video analysis status to success' do
          allow(BrayniacAI::FacialRecognition).to(
            receive(:find).and_return(BrayniacAI::FacialRecognition.new({ results: {} }))
          )

          subject.poll

          expect(video.reload).to be_analysis_success
        end
      end

      context 'when API results are not present' do
        it 'does nothing' do
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_return(BrayniacAI::FacialRecognition.new)

          subject.poll

          expect(video.reload).to be_analysis_pending
        end
      end

      context 'when API resource is not found' do
        it 'does nothing' do
          error = ActiveResource::ResourceNotFound.new('')
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(video.reload).to be_analysis_pending
        end
      end

      context 'when API server error occurs' do
        before do
          allow(Rails.logger).to receive(:error)
          allow(subject).to receive(:puts)
        end

        it 'does nothing' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(video.reload).to be_analysis_pending
        end

        it 'prints error message to logger' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(Rails.logger).to have_received(:error)
          expect(subject).to have_received(:puts)
        end
      end
    end

    describe '#expire' do
      let(:video) { create(:video, analysis_status: :pending, analysis_uid: "analysis_uid") }

      subject { PendingAnalysis::PendingVideoAnalysis.new(video.analysis_uid) }

      context 'when API results are present' do
        it 'updates the video analysis status to success' do
          allow(BrayniacAI::FacialRecognition).to(
            receive(:find).and_return(BrayniacAI::FacialRecognition.new({ results: {} }))
          )

          subject.expire

          expect(video.reload).to be_analysis_success
        end
      end

      context 'when API results are not present' do
        it 'updates the video analysis status to failure' do
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_return(BrayniacAI::FacialRecognition.new)

          subject.expire

          expect(video.reload).to be_analysis_failure
        end
      end

      context 'when API resource is not found' do
        it 'updates the video analysis status to failure' do
          error = ActiveResource::ResourceNotFound.new('')
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_raise(error)

          subject.expire

          expect(video.reload).to be_analysis_failure
        end
      end

      context 'when API server error occurs' do
        before do
          allow(Rails.logger).to receive(:error)
          allow(subject).to receive(:puts)
        end

        it 'does nothing' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(video.reload).to be_analysis_pending
        end

        it 'prints error message to logger' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::FacialRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(Rails.logger).to have_received(:error)
          expect(subject).to have_received(:puts)
        end
      end
    end
  end

  describe PendingAnalysis::PendingAudioAnalysis do
    describe '.poll' do
      it 'polls all videos with pending audio analysis' do 
        pending_analysis = create(:video, audio_analysis_status: :pending, audio_analysis_uid: "pending_uid")
        not_pending_analysis = create(:video, audio_analysis_status: :success, audio_analysis_uid: "not_pending_uid")

        allow(described_class).to receive(:new).and_return(spy)

        described_class.poll

        expect(described_class).to have_received(:new).with("pending_uid")
        expect(described_class).not_to have_received(:new).with("not_pending_uid")
      end
    end

    describe '.expire' do
      it 'expires all videos with pending analysis older than a given time' do
        pending_analysis_inside_range = create(:video, 
                                               audio_analysis_status: :pending, 
                                               audio_analysis_uid: "pending_inside_uid", 
                                               audio_analysis_started_at: 1.week.ago)
        pending_analysis_outside_range = create(:video, 
                                                audio_analysis_status: :pending, 
                                                audio_analysis_uid: "pending_outside_uid", 
                                                audio_analysis_started_at: 1.minute.ago)
        not_pending_analysis = create(:video, 
                                      analysis_status: :success, 
                                      analysis_uid: "not_pending_uid")

        allow(described_class).to receive(:new).and_return(spy)

        described_class.expire(1.day.ago)

        expect(described_class).to have_received(:new).with("pending_inside_uid")
        expect(described_class).not_to have_received(:new).with("pending_outside_uid")
        expect(described_class).not_to have_received(:new).with("not_pending_uid")
      end
    end

    describe '#poll' do
      let(:video) { create(:video, audio_analysis_status: :pending, audio_analysis_uid: "analysis_uid") }

      subject { PendingAnalysis::PendingAudioAnalysis.new(video.audio_analysis_uid) }

      context 'when API results are present' do
        it 'updates the audio analysis status to success' do
          allow(BrayniacAI::AudioRecognition).to(
            receive(:find).and_return(OpenStruct.new({ results: {} }))
          )

          subject.poll

          expect(video.reload).to be_audio_analysis_success
        end
      end

      context 'when API results are not present' do
        it 'does nothing' do
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_return(BrayniacAI::AudioRecognition.new)

          subject.poll

          expect(video.reload).to be_audio_analysis_pending
        end
      end

      context 'when API resource is not found' do
        it 'does nothing' do
          error = ActiveResource::ResourceNotFound.new('')
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(video.reload).to be_audio_analysis_pending
        end
      end

      context 'when API server error occurs' do
        before do
          allow(Rails.logger).to receive(:error)
          allow(subject).to receive(:puts)
        end

        it 'does nothing' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(video.reload).to be_audio_analysis_pending
        end

        it 'prints error message to logger' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(Rails.logger).to have_received(:error)
          expect(subject).to have_received(:puts)
        end
      end
    end

    describe '#expire' do
      let(:video) { create(:video, audio_analysis_status: :pending, audio_analysis_uid: "analysis_uid") }

      subject { PendingAnalysis::PendingAudioAnalysis.new(video.audio_analysis_uid) }

      context 'when API results are present' do
        it 'updates the audio analysis status to success' do
          allow(BrayniacAI::AudioRecognition).to(
            receive(:find).and_return(BrayniacAI::AudioRecognition.new({ results: {} }))
          )

          subject.expire

          expect(video.reload).to be_audio_analysis_success
        end
      end

      context 'when API results are not present' do
        it 'updates the audio analysis status to failure' do
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_return(BrayniacAI::AudioRecognition.new)

          subject.expire

          expect(video.reload).to be_audio_analysis_failure
        end
      end

      context 'when API resource is not found' do
        it 'updates the audio analysis status to failure' do
          error = ActiveResource::ResourceNotFound.new('')
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_raise(error)

          subject.expire

          expect(video.reload).to be_audio_analysis_failure
        end
      end

      context 'when API server error occurs' do
        before do
          allow(Rails.logger).to receive(:error)
          allow(subject).to receive(:puts)
        end

        it 'does nothing' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(video.reload).to be_audio_analysis_pending
        end

        it 'prints error message to logger' do
          error = ActiveResource::ServerError.new('')
          allow(BrayniacAI::AudioRecognition).to receive(:find).and_raise(error)

          subject.poll

          expect(Rails.logger).to have_received(:error)
          expect(subject).to have_received(:puts)
        end
      end
    end
  end
end
