# frozen_string_literal: true

RSpec.describe SemverDialects::Maven::Version do
  let(:version) { described_class.new([]) }

  describe '#compare_token_pair' do
    subject { version.send(:compare_token_pair, a, b) }

    let(:a) { 'A' }
    let(:b) { 'B' }

    using RSpec::Parameterized::TableSyntax

    # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
    where(:a, :b, :result) do
      1 | 1 | 0
      2 | 1 | 1
      1 | 2 | -1
      'a' | 'a' | 0
      'b' | 'a' | 1
      'a' | 'b' | -1
      1 | 'a' | 1
      'a' | 1 | -1
      SemverDialects::Maven::ALPHA | SemverDialects::Maven::ALPHA | 0
      1 | SemverDialects::Maven::ALPHA | 1
      'a' | SemverDialects::Maven::ALPHA | 1
      nil | SemverDialects::Maven::ALPHA | 1
      SemverDialects::Maven::BETA | SemverDialects::Maven::ALPHA | 1
      SemverDialects::Maven::MILESTONE | SemverDialects::Maven::BETA | 1
      SemverDialects::Maven::RC | SemverDialects::Maven::MILESTONE | 1
      SemverDialects::Maven::SNAPSHOT | SemverDialects::Maven::RC | 1
      SemverDialects::Maven::SP | 0 | 1
      'a' | SemverDialects::Maven::SP | 1
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

    with_them do
      it { is_expected.to eq(result) }

      context 'when arguments are swapped' do
        subject { version.send(:compare_token_pair, b, a) }

        it { is_expected.to eq(-result) }
      end
    end
  end
end

RSpec.describe SemverDialects::Maven::Version do
  describe '#to_s' do
    subject { version.to_s }

    let(:version) { described_class.new([1, 2, 3], addition: subversion) }
    let(:subversion) { described_class.new(['a'], addition: subsubversion) }
    let(:subsubversion) { described_class.new([1, 2]) }

    it { is_expected.to eq('1.2.3-a-1.2') }

    context 'without a prerelease tag' do
      let(:subversion) { nil }

      it { is_expected.to eq('1.2.3') }
    end
  end

  describe '#is_zero?' do
    using RSpec::Parameterized::TableSyntax

    # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
    where(:tokens, :addition_tokens, :result) do
      [0, 0, 1] | nil | false
      [0, 0, 0] | nil | true
      [0, 0, 0] | [0, 0, 1] | false
      [0, 0, 0] | [0, 0, 0] | true
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

    with_them do
      subject { version.is_zero? }

      let(:version) { described_class.new(tokens, addition:) }

      let(:addition) do
        return nil if addition_tokens.nil?

        described_class.new(addition_tokens)
      end

      it { is_expected.to eq(result) }
    end
  end
end

RSpec.describe SemverDialects::Maven::VersionParser do # rubocop:todo Metrics/BlockLength
  describe '.parse' do # rubocop:todo Metrics/BlockLength
    let(:version) { described_class.parse(input) }
    let(:input) { '1.2.3a1' }

    context 'with valid versions' do
      using RSpec::Parameterized::TableSyntax

      where(:input, :array) do
        '1.2.3' | [1, 2, 3]
        '1-z' | [1, ['z']]
        '1z' | [1, ['z']]
        '1-z.1' | [1, ['z', 1]]
        '1-z-1' | [1, ['z', [1]]]
        '1-z1' | [1, ['z', [1]]]
        '1-z-1.2' | [1, ['z', [1, 2]]]
        '1-z-1-2' | [1, ['z', [1, [2]]]]
        '1-z-1-2.3y' | [1, ['z', [1, [2, 3, ['y']]]]]
        '1ga1' | [1, [[1]]]
        '1-ga-2' | [1, [[2]]]
        '1_' | [1, ['_']]
        '1__' | [1, ['__']]
        '1__-' | [1, ['__']]
        '1_1' | [1, ['_', [1]]]
        '1_a' | [1, ['_a']]
        '9+181-r4173-14' | [9, ['+', [181, ['r', [4173, [14]]]]]]
        '9-+a' | [9, ['+a']]
      end

      with_them do
        it 'returns a version that has the expected array value' do
          expect(version.to_a).to eq(array)
        end
      end
    end

    context 'with valid versions' do
      using RSpec::Parameterized::TableSyntax

      where(:input, :normalized) do
        '1.2.3' | nil

        # non-numeric tokens
        '1.2.3-z' | nil
        '1.2.3-Z' | '1.2.3-z'
        '1.2.3z' | '1.2.3-z'
        '1.2.3Z' | '1.2.3-z'
        '1.2.3.z' | '1.2.3-z'
        '1.2.3.Z' | '1.2.3-z'

        # numeric tokens
        '1.2.3-z-1' | nil
        '1.2.3-z.1' | nil
        '1.2.3-z1' | '1.2.3-z-1'

        # alpha
        '1.2.3a' | '1.2.3-a'
        '1.2.3ax' | '1.2.3-ax'
        '1.2.3axyz0' | '1.2.3-axyz-0'
        '1.2.3a456' | '1.2.3-alpha-456'
        '1.2.3a1.2' | '1.2.3-alpha-1.2'
        '1.2.3alpha1.2' | '1.2.3-alpha-1.2'

        # beta
        '1.2.3b' | '1.2.3-b'
        '1.2.3bx' | '1.2.3-bx'
        '1.2.3bxyz0' | '1.2.3-bxyz-0'
        '1.2.3b456' | '1.2.3-beta-456'
        '1.2.3b1.2' | '1.2.3-beta-1.2'
        '1.2.3beta1.2' | '1.2.3-beta-1.2'

        # milestone
        '1.2.3m' | '1.2.3-m'
        '1.2.3mx' | '1.2.3-mx'
        '1.2.3mxyz0' | '1.2.3-mxyz-0'
        '1.2.3m456' | '1.2.3-milestone-456'
        '1.2.3m1.2' | '1.2.3-milestone-1.2'
        '1.2.3milestone1.2' | '1.2.3-milestone-1.2'

        # rc
        '1.2.3rc' | '1.2.3-rc'
        '1.2.3rcx' | '1.2.3-rcx'
        '1.2.3rcxyz0' | '1.2.3-rcxyz-0'
        '1.2.3rc456' | '1.2.3-rc-456'
        '1.2.3rc1.2' | '1.2.3-rc-1.2'
        '1.2.3cr1.2' | '1.2.3-rc-1.2'

        # snapshot
        '1.2.3snapshot' | '1.2.3-snapshot'
        '1.2.3snapshotx' | '1.2.3-snapshotx'
        '1.2.3snapshotxyz0' | '1.2.3-snapshotxyz-0'
        '1.2.3snapshot456' | '1.2.3-snapshot-456'
        '1.2.3snapshot1.2' | '1.2.3-snapshot-1.2'

        # final
        '1.2.3final' | '1.2.3'
        '1.2.3final1' | '1.2.3-1'
        '1.2.3finala' | '1.2.3-finala'

        # ga
        '1.2.3ga' | '1.2.3'
        '1.2.3ga-1' | '1.2.3-1'
        '1.2.3gaa' | '1.2.3-gaa'

        # release
        '1.2.3release' | '1.2.3'
        '1.2.3release1' | '1.2.3-1'
        '1.2.3releasea' | '1.2.3-releasea'

        # underscores
        '0.0.62.Final-linux-x86_64' | '0.0.62-linux-x-86-_-64'
        '2.11.3-spark_3.3' | '2.11.3-spark-_-3.3'

        # plus sign
        '9+181-r4173-14' | '9-+-181-r-4173-14'
        '9+a' | '9-+a'
      end

      with_them do
        it 'returns a version that has a normalized string value' do
          expect(version.to_s).to eq(normalized || input)
        end
      end
    end

    context 'with invalid versions' do
      using RSpec::Parameterized::TableSyntax

      where(:input, :rest) do
        '1.2.$' | '$'
        'com.google.guava:guava:33.0.0-jre' | ':guava:33.0.0-jre'
      end

      with_them do
        it 'raises an error' do
          expect do
            version.to_s
          end.to raise_error(an_instance_of(SemverDialects::IncompleteScanError).and(having_attributes(rest:)))
        end
      end

      context 'when version produces no tokens' do
        let(:input) { '-' }
        it 'raises an invalid version error' do
          expect { version }.to raise_error(SemverDialects::InvalidVersionError, "invalid version '-'")
        end
      end
    end
  end
end

RSpec.describe SemverDialects::Maven::Version do
  describe '#<=>' do
    using RSpec::Parameterized::TableSyntax

    subject { version.<=>(other_version) }
    let(:version) { SemverDialects::Maven::VersionParser.parse(a) }
    let(:other_version) { SemverDialects::Maven::VersionParser.parse(b) }

    # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
    where(:a, :b, :result) do
      '1.2.3' | '1.2.2' | 1
      '1.2.3' | '1.2.3' | 0
      '1.2.0' | '1.2' | 0
      '1.2' | '1.2.0' | 0

      '1.2.3-0' | '1.2.3-a0' | 1
      '1.2.3-0' | '1.2.3-a0' | 1
      '1.2.3-0' | '1.2.3-a' | -1
      '1.2.3-0' | '1.2.3-1' | -1
      '1.2.3-0' | '1.2.3-0' | 0
      '1.2.3-0' | '1.2.3-0-1' | -1
      '1.2.3-0' | '1.2.3-0.1' | -1

      '1.2.3' | '1.2.3-a0' | 1
      '1.2.3' | '1.2.3-a' | -1
      '1.2.3' | '1.2.3-1' | -1
      '1.2.3' | '1.2.3-0' | 0
      '1.2.3' | '1.2.3-0-1' | -1
      '1.2.3' | '1.2.3-0.1' | -1

      '1alpha-0' | '1a0' | 0
      '1alpha-0' | '1alpha' | 0
      '1alpha-0' | '1alpha0' | 0
      '1alpha-0' | '1alpha.0' | 0
      '1alpha-0' | '1alpha.z' | -1
      '1alpha.z' | '1alpha-z' | 0
      '1alpha-z' | '1alpha1' | -1
      '1alpha1' | '1alpha-1' | 0
      '1alpha-1' | '1alpha.1' | -1
      '1alpha.1' | '1beta' | -1

      '1beta-0' | '1b0' | 0
      '1beta-0' | '1beta' | 0
      '1beta-0' | '1beta0' | 0
      '1beta-0' | '1beta.0' | 0
      '1beta-0' | '1beta.z' | -1
      '1beta.z' | '1beta-z' | 0
      '1beta-z' | '1beta1' | -1
      '1beta1' | '1beta-1' | 0
      '1beta-1' | '1beta.1' | -1
      '1beta.1' | '1milestone' | -1

      '1milestone-0' | '1m0' | 0
      '1milestone-0' | '1milestone' | 0
      '1milestone-0' | '1milestone0' | 0
      '1milestone-0' | '1milestone.0' | 0
      '1milestone-0' | '1milestone.z' | -1
      '1milestone.z' | '1milestone-z' | 0
      '1milestone-z' | '1milestone1' | -1
      '1milestone1' | '1milestone-1' | 0
      '1milestone-1' | '1milestone.1' | -1
      '1milestone.1' | '1rc' | -1

      '1rc-0' | '1rc0' | 0
      '1rc-0' | '1cr' | 0
      '1rc-0' | '1rc' | 0
      '1rc-0' | '1rc0' | 0
      '1rc-0' | '1rc.0' | 0
      '1rc-0' | '1rc.z' | -1
      '1rc.z' | '1rc-z' | 0
      '1rc-z' | '1rc1' | -1
      '1rc1' | '1rc-1' | 0
      '1rc-1' | '1rc.1' | -1
      '1rc.1' | '1snapshot' | -1

      '1snapshot-0' | '1snapshot0' | 0
      '1snapshot-0' | '1snapshot' | 0
      '1snapshot-0' | '1snapshot0' | 0
      '1snapshot-0' | '1snapshot.0' | 0
      '1snapshot-0' | '1snapshot.z' | -1
      '1snapshot.z' | '1snapshot-z' | 0
      '1snapshot-z' | '1snapshot1' | -1
      '1snapshot1' | '1snapshot-1' | 0
      '1snapshot-1' | '1snapshot.1' | -1
      '1snapshot.1' | '1' | -1

      '1' | '1sp0' | -1
      '1sp-0' | '1sp0' | 0
      '1sp-0' | '1sp' | 0
      '1sp-0' | '1sp0' | 0
      '1sp-0' | '1sp.0' | 0
      '1sp-0' | '1sp.z' | -1
      '1sp.z' | '1sp-z' | 0
      '1sp-z' | '1sp1' | -1
      '1sp1' | '1sp-1' | 0
      '1sp-1' | '1sp.1' | -1
      '1sp.1' | '1-a' | -1

      '1-a' | '1a' | 0
      '1-a' | '1.a' | 0
      '1-a' | '1-b' | -1
      '1-b' | '1-b-1' | -1
      '1-b-1' | '1-b.1' | -1
      '1-b.1' | '1-b.2' | -1
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

    with_them do
      it { is_expected.to eq(result) }

      context 'when arguments are swapped' do
        subject { other_version.<=>(version) }

        it { is_expected.to eq(-result) }
      end
    end
  end
end
