# frozen_string_literal: true
# rubocop:todo all

require 'spec_helper'

describe 'Matcher operators' do
  let(:result) do
    document._matches?(query)
  end

  shared_examples 'is true' do
    it 'is true' do
      result.should be true
    end
  end

  shared_examples 'is false' do
    it 'is false 'do
      result.should be false
    end
  end

  context 'when comparing Time objects' do
    let(:time) {Time.utc(2021, 10, 25, 10, 30, 30, 581345)}
    let(:document) do
      ConsumptionPeriod.new(:started_at => time)
    end

    context 'comparing millisecond precision' do
      let(:time_millis) {Time.utc(2021, 10, 25, 10, 30, 30, 581774)}

      context 'with exact match' do
        let(:query) do
          {'started_at' => time_millis}
        end

        it_behaves_like 'is true'

        context 'and query has different timezone' do
          let(:time_millis) do
            Time.utc(2021, 10, 25, 10, 30, 30, 581345).in_time_zone("Stockholm")
          end

          it_behaves_like 'is true'
        end
      end

      context 'with $in' do
        let(:query) do
          {'started_at' => {:$in => [time_millis]}}
        end

        it_behaves_like 'is true'
      end

      context 'when matching an element in an array' do
        let(:document) do
          Mop.new(:array_field => [time])
        end

        context 'with equals match' do
          let(:query) do
            {'array_field' => time_millis}
          end

          it_behaves_like 'is true'
        end
      end
    end
  end

  context 'when querying nested document' do
    context 'embeds_one' do
      let(:document) do
        Canvas.new(
          writer: Writer.new(speed: 3),
        )
      end

      context 'implicit $eq' do
        context 'matches' do
          let(:query) do
            {'writer.speed' => 3}
          end

          it_behaves_like 'is true'
        end

        context 'does not match' do
          let(:query) do
            {'speed' => 3}
          end

          it_behaves_like 'is false'
        end
      end

      context 'field operator' do
        # Test string and symbol operators
        ['$gt', :$gt].each do |op|
          context op.inspect do
            context 'matches' do
              let(:query) do
                {'writer.speed' => {op => 2}}
              end

              it_behaves_like 'is true'
            end

            context 'does not match' do
              let(:query) do
                {'writer.speed' => {op => 3}}
              end

              it_behaves_like 'is false'
            end
          end
        end
      end

      context 'hash query' do
        context 'scalar field in embedded document' do
          context 'matches' do
            let(:query) do
              {writer: {speed: 3}}
            end

            it_behaves_like 'is true'
          end

          context 'does not match' do
            let(:query) do
              {writer: {slow: 3}}
            end

            it_behaves_like 'is false'
          end
        end

        context 'hash field' do
          let(:document) do
            Bar.new(
              writer: {speed: 3},
            )
          end

          context 'matches' do
            let(:query) do
              {writer: {speed: 3}}
            end

            it_behaves_like 'is true'
          end

          context 'does not match' do
            let(:query) do
              {writer: {slow: 3}}
            end

            it_behaves_like 'is false'
          end
        end
      end
    end

    context 'embeds_many' do
      let(:document) do
        Survey.new(
          questions: [Question.new(
            answers: [
              Answer.new(position: 3),
              Answer.new(position: 4),
            ],
          )],
        )
      end

      context 'implicit $eq' do
        context 'matches' do
          let(:query) do
            {'questions.answers.position' => 3}
          end

          it_behaves_like 'is true'
        end

        context 'does not match' do
          let(:query) do
            {'questions.answers.position' => 2}
          end

          it_behaves_like 'is false'
        end
      end

      context 'field operator' do
        # Test string and symbol operators
        ['$gt', :$gt].each do |op|
          context op.inspect do
            context 'matches' do
              let(:query) do
                {'questions.answers.position' => {op => 2}}
              end

              it_behaves_like 'is true'
            end

            context 'does not match' do
              let(:query) do
                {'questions.answers.position' => {op => 4}}
              end

              it_behaves_like 'is false'
            end
          end
        end
      end

      context 'array index' do
        context 'matches' do
          let(:query) do
            {'questions.answers.1' => {position: 4}}
          end

          it_behaves_like 'is true'
        end

        context 'does not match' do
          let(:query) do
            {'questions.answers.0' => {position: 4}}
          end

          it_behaves_like 'is false'
        end
      end
    end
  end
end
