# frozen_string_literal: true

RSpec.describe SemverDialects::Semver2::PrereleaseTag do
  let(:prerelease_tag) { described_class.new([]) }

  describe '#compare_token_pair' do
    subject { prerelease_tag.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
      'a' | 1   | 1
      1   | 'a' | -1
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

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

RSpec.describe SemverDialects::Semver2::Version do
  let(:version) { described_class.new(tokens, prerelease_tag:) }
  let(:tokens) { [1, 2, 3] }
  let(:prerelease_tag) { SemverDialects::Semver2::PrereleaseTag.new(prerelease_tag_tokens) }
  let(:prerelease_tag_tokens) { ['alpha', 1, 'beta', 2] }

  describe '#to_s' do
    subject { version.to_s }

    it { is_expected.to eq('1.2.3-alpha.1.beta.2') }

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

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

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

    where(:tokens, :prerelease_tokens, :result) do
      [0, 0, 1] | nil | false
      [0, 0, 0] | nil | true
      [0, 0, 0] | [0, 0, 1] | false
      [0, 0, 0] | [0] | false
    end

    with_them do
      subject { version.is_zero? }

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

      let(:prerelease_tag) do
        return nil if prerelease_tokens.nil?

        SemverDialects::Semver2::PrereleaseTag.new(prerelease_tokens)
      end

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

RSpec.describe SemverDialects::Semver2::VersionParser do
  describe '.parse' do
    let(:version) { described_class.parse(input) }
    let(:input) { '1.2.3-alpha.1' }

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

      where(:input, :normalized) do
        '1.2.3' | nil
        '1.2.x' | nil
        '1.2.3+build' | '1.2.3'
        '1.2.3-alpha' | nil
        '1.2.3-alpha+build' | '1.2.3-alpha'
        '1.2.3-alpha.1' | nil
        '1.2.3-alpha.1.beta.2' | nil
        '1.2.3-alpha.1.beta.2+build1235-999++' | '1.2.3-alpha.1.beta.2'
        'v1.2.3-v' | '1.2.3-v'
      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

      # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands
      where(:input, :rest) do
        'x.2' | 'x.2'
        '1.x.3' | 'x.3'
        '1.2.3.a' | 'a'
        '1.2.$' | '$'
        'vv1.2.3' | 'v1.2.3'
      end
      # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

      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
    end
  end
end

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

    subject { version.<=>(other_version) }
    let(:version) { SemverDialects::Semver2::VersionParser.parse(a) }
    let(:other_version) { SemverDialects::Semver2::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' | '1.2.3-alpha' | 1
      '1.2.3-alpha' | '1.2.3' | -1
      '1.2.3-alpha.1' | '1.2.3-alpha' | 1
      '1.2.3-alpha.0' | '1.2.3-alpha' | 1
      '1.2.3-alpha.1.beta' | '1.2.3-alpha' | 1
      '1.2.3-alpha.1.beta.2' | '1.2.3-alpha.1.beta.1' | 1
      '16.0.0-272-10' | '16.0.0-272-9' | -1
      '2.0' | '1.x' | 1
      '1.2.3' | '1.x' | 0
      '1.2.3' | '1.2.x' | 0
      '1.3' | '1.2.x' | 1
      '1.2.0-alpha.1' | '1.2.x' | 0
    end
    # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands

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

      it 'gives opposite result when arguments are swapped' do
        expect(other_version.<=>(version)).to eq(-result)
      end
    end
  end
end
