# frozen_string_literal: true

RSpec.describe RuboCop::Cop::Metrics::BlockLength, :config do
  subject(:cop) { described_class.new(config) }

  let(:cop_config) { { 'Max' => 2, 'CountComments' => false } }

  shared_examples 'ignoring an offense on an excluded method' do |excluded|
    before { cop_config['ExcludedMethods'] = [excluded] }

    it 'still rejects other methods with long blocks' do
      expect_offense(<<-RUBY.strip_indent)
        something do
        ^^^^^^^^^^^^ Block has too many lines. [3/2]
          a = 1
          a = 2
          a = 3
        end
      RUBY
    end

    it 'accepts the foo method with a long block' do
      expect_no_offenses(<<-RUBY.strip_indent)
        #{excluded} do
          a = 1
          a = 2
          a = 3
        end
      RUBY
    end
  end

  it 'rejects a block with more than 5 lines' do
    expect_offense(<<-RUBY.strip_indent)
      something do
      ^^^^^^^^^^^^ Block has too many lines. [3/2]
        a = 1
        a = 2
        a = 3
      end
    RUBY
  end

  it 'reports the correct beginning and end lines' do
    inspect_source(<<-RUBY.strip_indent)
      something do
        a = 1
        a = 2
        a = 3
      end
    RUBY
    offense = cop.offenses.first
    expect(offense.location.first_line).to eq(1)
    expect(offense.location.last_line).to eq(5)
  end

  it 'accepts a block with less than 3 lines' do
    expect_no_offenses(<<-RUBY.strip_indent)
      something do
        a = 1
        a = 2
      end
    RUBY
  end

  it 'does not count blank lines' do
    expect_no_offenses(<<-RUBY.strip_indent)
      something do
        a = 1


        a = 4
      end
    RUBY
  end

  it 'accepts a block with multiline receiver and less than 3 lines of body' do
    expect_no_offenses(<<-RUBY.strip_indent)
      [
        :a,
        :b,
        :c,
      ].each do
        a = 1
        a = 2
      end
    RUBY
  end

  it 'accepts empty blocks' do
    expect_no_offenses(<<-RUBY.strip_indent)
      something do
      end
    RUBY
  end

  it 'rejects brace blocks too' do
    expect_offense(<<-RUBY.strip_indent)
      something {
      ^^^^^^^^^^^ Block has too many lines. [3/2]
        a = 1
        a = 2
        a = 3
      }
    RUBY
  end

  it 'properly counts nested blocks' do
    expect_offense(<<-RUBY.strip_indent)
      something do
      ^^^^^^^^^^^^ Block has too many lines. [6/2]
        something do
        ^^^^^^^^^^^^ Block has too many lines. [4/2]
          a = 2
          a = 3
          a = 4
          a = 5
        end
      end
    RUBY
  end

  it 'does not count commented lines by default' do
    expect_no_offenses(<<-RUBY.strip_indent)
      something do
        a = 1
        #a = 2
        #a = 3
        a = 4
      end
    RUBY
  end

  context 'when CountComments is enabled' do
    before { cop_config['CountComments'] = true }

    it 'also counts commented lines' do
      expect_offense(<<-RUBY.strip_indent)
        something do
        ^^^^^^^^^^^^ Block has too many lines. [3/2]
          a = 1
          #a = 2
          a = 3
        end
      RUBY
    end
  end

  context 'when ExcludedMethods is enabled' do
    it_behaves_like('ignoring an offense on an excluded method', 'foo')

    it_behaves_like('ignoring an offense on an excluded method',
                    'Gem::Specification.new')

    context 'when receiver contains whitespaces' do
      before { cop_config['ExcludedMethods'] = ['Foo::Bar.baz'] }

      it 'ignores whitespaces' do
        expect_no_offenses(<<-RUBY.strip_indent)
          Foo::
            Bar.baz do
            a = 1
            a = 2
            a = 3
          end
        RUBY
      end
    end

    context 'when a method is ignored, but receiver is a module' do
      before { cop_config['ExcludedMethods'] = ['baz'] }

      it 'does not report an offense' do
        expect_no_offenses(<<-RUBY.strip_indent)
          Foo::Bar.baz do
            a = 1
            a = 2
            a = 3
          end
        RUBY
      end
    end
  end
end
