shared_examples "a sequence" do |options|
  first_value = options[:first_value]
  second_value = options[:second_value]

  it "has a next value equal to its first value" do
    expect(subject.next).to eq first_value
  end

  it "has a next value equal to the 2nd value after being incremented" do
    subject.next

    expect(subject.next).to eq second_value
  end

  it "has a next value equal to the 1st value after rewinding" do
    subject.next
    subject.rewind

    expect(subject.next).to eq first_value
  end
end

describe FactoryBot::Sequence do
  describe "a basic sequence" do
    let(:name) { :test }
    subject { FactoryBot::Sequence.new(name) { |n| "=#{n}" } }

    its(:name) { should eq name }
    its(:names) { should eq [name] }

    it_behaves_like "a sequence", first_value: "=1", second_value: "=2"
  end

  describe "a custom sequence" do
    subject { FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}" } }

    it_behaves_like "a sequence", first_value: "=A", second_value: "=B"
  end

  describe "a sequence with aliases using default value" do
    subject do
      FactoryBot::Sequence.new(:test, aliases: [:alias, :other]) do |n|
        "=#{n}"
      end
    end

    it "has the expected names as its names" do
      names = [:foo, :bar, :baz]
      sequence = FactoryBot::Sequence.new(names.first, aliases: names.last(2)) {
        "=#{n}"
      }

      expect(sequence.names).to eq names
    end

    it_behaves_like "a sequence", first_value: "=1", second_value: "=2"
  end

  describe "a sequence with custom value and aliases" do
    subject do
      FactoryBot::Sequence.new(:test, 3, aliases: [:alias, :other]) do |n|
        "=#{n}"
      end
    end

    it "has the expected names as its names" do
      names = [:foo, :bar, :baz]
      sequence = FactoryBot::Sequence.new(names.first, 3, aliases: names.last(2)) {
        "=#{n}"
      }
      expect(sequence.names).to eq names
    end

    it_behaves_like "a sequence", first_value: "=3", second_value: "=4"
  end

  describe "a basic sequence without a block" do
    subject { FactoryBot::Sequence.new(:name) }

    it_behaves_like "a sequence", first_value: 1, second_value: 2
  end

  describe "a custom sequence without a block" do
    subject { FactoryBot::Sequence.new(:name, "A") }

    it_behaves_like "a sequence", first_value: "A", second_value: "B"
  end

  describe "iterating over items in an enumerator" do
    subject do
      FactoryBot::Sequence.new(:name, %w[foo bar].to_enum) { |n| "=#{n}" }
    end

    it "navigates to the next items until no items remain" do
      sequence = FactoryBot::Sequence.new(:name, %w[foo bar].to_enum) { |n| "=#{n}" }
      expect(sequence.next).to eq "=foo"
      expect(sequence.next).to eq "=bar"

      expect { sequence.next }.to raise_error(StopIteration)
    end

    it_behaves_like "a sequence", first_value: "=foo", second_value: "=bar"
  end

  it "a custom sequence and scope increments within the correct scope" do
    sequence = FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}#{foo}" }
    scope = double("scope", foo: "attribute")

    expect(sequence.next(scope)).to eq "=Aattribute"
  end

  it "a custom sequence and scope increments within the correct scope when incrementing" do
    sequence = FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}#{foo}" }
    scope = double("scope", foo: "attribute")
    sequence.next(scope)

    expect(sequence.next(scope)).to eq "=Battribute"
  end

  it "a custom scope increments within the correct scope after rewinding" do
    sequence = FactoryBot::Sequence.new(:name, "A") { |n| "=#{n}#{foo}" }
    scope = double("scope", foo: "attribute")
    sequence.next(scope)
    sequence.rewind

    expect(sequence.next(scope)).to eq "=Aattribute"
  end
end
