require "pact_broker/pacts/repository"

module PactBroker
  module Pacts
    describe Repository do
      describe "#find_for_verification" do
        def find_by_consumer_version_number(consumer_version_number)
          subject.find{ |pact| pact.consumer_version_number == consumer_version_number }
        end

        def find_by_consumer_name_and_consumer_version_number(consumer_name, consumer_version_number)
          subject.find{ |pact| pact.consumer_name == consumer_name && pact.consumer_version_number == consumer_version_number }
        end

        subject { Repository.new.find_for_verification("Bar", consumer_version_selectors) }

        context "when there are no selectors" do
          let(:foo_main_branch) { nil }

          let(:consumer_version_selectors) { Selectors.new }

          context "when there is no main branch version" do
            before do
              td.create_consumer("Foo", main_branch: "main")
                .create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
                .create_consumer_version_tag("prod")
                .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
                .comment("next pact not selected")
                .create_pact
                .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
                .create_pact
                .create_consumer("Baz")
                .create_consumer_version("baz-latest-dev-version", tag_names: ["dev", "main"])
                .create_pact
            end

            it "returns the latest pact for each consumer" do
              expect(subject.size).to eq 2
              expect(subject).to include(have_attributes(consumer_name: "Foo", consumer_version_number: "foo-latest-dev-version"))
              expect(subject).to include(have_attributes(consumer_name: "Baz", consumer_version_number: "baz-latest-dev-version"))
              expect(subject.all?(&:overall_latest?)).to be true
            end
          end

          context "when there is a version from the main branch" do
            before do
              td.create_consumer("Foo", main_branch: "main")
                .create_consumer_version("1", branch: "main")
                .create_provider("Bar")
                .create_pact
                .create_pact_with_hierarchy("Foo", "2", "Bar")
            end

            it "returns the latest version from the main branch" do
              expect(subject.size).to eq 1
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to_not be_nil
              expect(subject.first.selectors.first).to be_latest_for_main_branch
            end
          end

          context "when there is a version with a tag with the name of the main branch" do
            before do
              td.create_consumer("Foo", main_branch: "main")
                .create_consumer_version("1", tag_name: "main")
                .create_provider("Bar")
                .create_pact
                .create_pact_with_hierarchy("Foo", "2", "Bar")
            end

            it "returns the latest version from the main branch" do
              expect(subject.size).to eq 1
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to_not be_nil
              expect(subject.first.selectors.first).to be_latest_for_tag
              expect(subject.first.selectors.first.tag).to eq "main"
            end
          end

          context "when there is a not version from the main branch" do
            before do
              td.create_pact_with_hierarchy("Foo", "1", "Bar")
                .create_pact_with_hierarchy("Foo", "2", "Bar")
            end

            it "returns the latest version from the main branch" do
              expect(subject.size).to eq 1
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "2")).to_not be_nil
              expect(subject.first.selectors.first).to be_overall_latest
            end
          end

          context "when there are currently deployed versons" do
            before do
              td.create_environment("test")
                .create_pact_with_hierarchy("Foo", "1", "Bar")
                .create_deployed_version_for_consumer_version(currently_deployed: false)
                .create_pact_with_hierarchy("Foo", "2", "Bar")
                .create_deployed_version_for_consumer_version
                .create_pact_with_hierarchy("Foo", "3", "Bar")
            end

            it "returns the currently deployed pacts" do
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to be_nil
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "2")).to_not be_nil
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "2").selectors.first).to be_currently_deployed
            end
          end

          context "when there are currently released+supported versions" do
            before do
              td.create_environment("test")
                .create_pact_with_hierarchy("Foo", "1", "Bar")
                .create_released_version_for_consumer_version(currently_supported: false)
                .create_pact_with_hierarchy("Foo", "2", "Bar")
                .create_released_version_for_consumer_version
                .create_pact_with_hierarchy("Foo", "3", "Bar")
            end

            it "returns the currently deployed pacts" do
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "1")).to be_nil
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "2")).to_not be_nil
              expect(find_by_consumer_name_and_consumer_version_number("Foo", "2").selectors.first).to be_currently_supported
            end
          end
        end

        context "when the selector is latest: true" do
          let(:pact_selector_1) { Selector.overall_latest }
          let(:consumer_version_selectors) do
            Selectors.new(pact_selector_1)
          end

          before do
            td.create_pact_with_hierarchy("Foo1", "1", "Bar")
              .create_pact_with_hierarchy("Foo1", "2", "Bar")
              .create_pact_with_hierarchy("Foo2", "3", "Bar")
              .create_pact_with_hierarchy("Foo2", "4", "Bar2")
          end

          it "returns the latest pact for each consumer" do
            expect(subject.size).to eq 2
            expect(find_by_consumer_name_and_consumer_version_number("Foo1", "2").selectors).to eq [Selector.overall_latest.resolve(PactBroker::Domain::Version.find(number: "2"))]
            expect(find_by_consumer_name_and_consumer_version_number("Foo2", "3").selectors).to eq [Selector.overall_latest.resolve(PactBroker::Domain::Version.find(number: "3"))]
          end
        end

        context "when the selector is latest: true for a particular consumer" do
          let(:pact_selector_1) { Selector.latest_for_consumer("Foo1") }

          let(:consumer_version_selectors) do
            Selectors.new(pact_selector_1)
          end

          before do
            td.create_pact_with_hierarchy("Foo1", "1", "Bar")
              .create_pact_with_hierarchy("Foo1", "2", "Bar")
              .create_pact_with_hierarchy("Foo2", "2", "Bar")
              .create_pact_with_hierarchy("Foo2", "2", "Bar2")
          end

          it "returns the latest pact for each consumer" do
            expect(subject.size).to eq 1
            expect(find_by_consumer_name_and_consumer_version_number("Foo1", "2").selectors).to eq [pact_selector_1.resolve(PactBroker::Domain::Version.for("Foo1", "2"))]
          end
        end

        context "when the selector is latest: true, with a tag, for a particular consumer" do
          let(:pact_selector_1) { Selector.latest_for_tag_and_consumer("prod", "Foo1") }

          let(:consumer_version_selectors) do
            Selectors.new(pact_selector_1)
          end

          before do
            td.create_pact_with_hierarchy("Foo1", "1", "Bar")
              .create_consumer_version_tag("prod")
              .create_pact_with_hierarchy("Foo1", "2", "Bar")
              .create_pact_with_hierarchy("Foo2", "2", "Bar")
              .create_consumer_version_tag("prod")
              .create_pact_with_hierarchy("Foo2", "2", "Bar2")
          end

          it "returns the latest pact for each consumer" do
            expect(subject.size).to eq 1
            expected_consumer_version = PactBroker::Domain::Version.where_pacticipant_name("Foo1").where(number: "1").single_record
            expect(find_by_consumer_name_and_consumer_version_number("Foo1", "1").selectors).to eq [pact_selector_1.resolve(expected_consumer_version)]
          end
        end

        context "when the latest consumer tag names are specified" do
          before do
            td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
              .create_consumer_version_tag("prod")
              .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
              .comment("next pact not selected")
              .create_pact
              .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
              .create_pact
              .create_consumer("Baz")
              .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
              .create_consumer_version_tag("prod")
              .create_pact
          end

          let(:pact_selector_1) { Selector.latest_for_tag("dev") }
          let(:pact_selector_2) { Selector.latest_for_tag("prod") }
          let(:consumer_version_selectors) do
            Selectors.new(pact_selector_1, pact_selector_2)
          end
          let(:expected_sorted_selectors) do
            [
              ResolvedSelector.new({ tag: "dev", latest: true }, PactBroker::Domain::Version.for("Baz", "baz-latest-dev-version")),
              ResolvedSelector.new({ tag: "prod", latest: true }, PactBroker::Domain::Version.for("Baz", "baz-latest-dev-version"))
            ]
          end

          it "returns the latest pact with the specified tags for each consumer" do
            expect(find_by_consumer_version_number("foo-latest-prod-version").selectors).to eq [Selector.latest_for_tag("prod").resolve(PactBroker::Domain::Version.for("Foo", "foo-latest-prod-version"))]
            expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [Selector.latest_for_tag("dev").resolve(PactBroker::Domain::Version.for("Foo", "foo-latest-dev-version"))]
            expect(find_by_consumer_version_number("baz-latest-dev-version").selectors.sort_by{ |s| s[:tag] }).to eq expected_sorted_selectors
            expect(subject.size).to eq 3
          end

          it "sets the tags" do
            expect(find_by_consumer_version_number("foo-latest-prod-version").selectors.collect(&:tag)).to eq ["prod"]
          end

          context "when a consumer name is specified" do
            before do
              td.create_pact_with_hierarchy("Foo", "2", "Bar")
                .create_consumer_version_tag("prod")
                .create_pact_with_hierarchy("Foo", "3", "Bar")
                .create_consumer_version_tag("prod")
                .create_consumer_version("4")
                .create_consumer_version_tag("prod")
                .republish_same_pact
            end

            let(:consumer_version_selectors) do
              Selectors.new(Selector.all_for_tag_and_consumer("prod", "Foo"))
            end

            it "returns all the pacts with that tag for that consumer" do
              expect(subject.size).to eq 3
              expect(find_by_consumer_version_number("foo-latest-prod-version").selectors).to eq [Selector.all_for_tag_and_consumer("prod", "Foo").resolve(PactBroker::Domain::Version.for("Foo", "foo-latest-prod-version"))]
            end

            it "includes all the selectors when the same pact content is selected multiple times (used to just use the latest, not sure about this)" do
              expect(find_by_consumer_version_number("3")).to be nil
              expect(find_by_consumer_version_number("4").selectors.first).to eq Selector.all_for_tag_and_consumer("prod", "Foo").resolve(PactBroker::Domain::Version.for("Foo", "3"))
              expect(find_by_consumer_version_number("4").selectors.last).to eq Selector.all_for_tag_and_consumer("prod", "Foo").resolve(PactBroker::Domain::Version.for("Foo", "4"))
            end
          end
        end

        context "when all versions with a given tag are requested" do
          before do
            td.create_pact_with_hierarchy("Foo2", "prod-version-1", "Bar2")
              .create_consumer_version_tag("prod")
              .create_consumer_version("not-prod-version", tag_names: %w[master])
              .create_pact
              .create_consumer_version("prod-version-2", tag_names: %w[prod])
              .create_pact
          end

          let(:consumer_version_selectors) { Selectors.new(pact_selector_1) }
          let(:pact_selector_1) { Selector.all_for_tag("prod") }

          subject { Repository.new.find_for_verification("Bar2", consumer_version_selectors) }

          it "returns all the versions with the specified tag" do
            expect(subject.size).to be 2
            expect(find_by_consumer_version_number("prod-version-1").selectors.collect(&:tag)).to eq %w[prod]
            expect(find_by_consumer_version_number("prod-version-2").selectors.collect(&:tag)).to eq %w[prod]
          end

          it "dedupes them to ensure that each pact version is only verified once" do
            td.create_consumer_version("prod-version-3", tag_names: %w[prod])
              .republish_same_pact
            expect(subject.size).to be 2
            expect(subject.collect(&:consumer_version_number).sort).to eq %w[prod-version-1 prod-version-3]
          end
        end

        context "when all versions with a given tag for a given consumer are requested" do
          before do
            td.create_pact_with_hierarchy("Foo2", "prod-version-1", "Bar2")
              .create_consumer_version_tag("prod")
              .create_consumer_version("not-prod-version", tag_names: %w[master])
              .create_pact
              .create_consumer_version("prod-version-2", tag_names: %w[prod])
              .create_pact
              .create_consumer("Foo3")
              .create_consumer_version("prod-version-3", tag_names: %w[prod])
              .create_pact
          end

          let(:consumer_version_selectors) { Selectors.new(pact_selector_1) }
          let(:pact_selector_1) { Selector.all_for_tag_and_consumer("prod", "Foo2") }

          subject { Repository.new.find_for_verification("Bar2", consumer_version_selectors) }

          it "returns all the versions with the specified tag and consumer" do
            expect(subject.size).to be 2
            expect(find_by_consumer_version_number("prod-version-1")).to_not be nil
            expect(find_by_consumer_version_number("prod-version-2")).to_not be nil
            expect(find_by_consumer_version_number("prod-version-3")).to be nil
          end

          it "sets the selectors" do
            expect(find_by_consumer_version_number("prod-version-1").selectors.first.tag).to eq "prod"
            expect(find_by_consumer_version_number("prod-version-1").selectors.first.consumer).to eq "Foo2"
          end
        end

        context "when a pact version has been selected by two different selectors" do
          before do
            td.create_pact_with_hierarchy("Foo", "1", "Bar")
              .create_consumer_version_tag("dev")
              .create_consumer_version_tag("prod")
          end

          let(:pact_selector_1) { Selector.all_for_tag("prod") }
          let(:pact_selector_2) { Selector.latest_for_tag("dev") }
          let(:consumer_version_selectors) { Selectors.new(pact_selector_1, pact_selector_2) }

          it "returns a single selected pact with multiple selectors" do
            expect(subject.size).to eq 1
            expect(subject.first.selectors.size).to eq 2
          end
        end

        context "when no selectors are specified" do
          before do
            td.create_pact_with_hierarchy("Foo", "foo-latest-prod-version", "Bar")
              .create_consumer_version_tag("prod")
              .create_consumer_version("not-latest-dev-version", tag_names: ["dev"])
              .comment("next pact not selected")
              .create_pact
              .create_consumer_version("foo-latest-dev-version", tag_names: ["dev"])
              .create_pact
              .create_consumer("Baz")
              .create_consumer_version("baz-latest-dev-version", tag_names: ["dev"])
              .create_pact
          end

          let(:consumer_version_selectors) { Selectors.new }

          it "returns the latest pact for each provider" do
            expect(find_by_consumer_version_number("foo-latest-dev-version")).to_not be nil
            expect(find_by_consumer_version_number("baz-latest-dev-version")).to_not be nil
            expect(subject.size).to eq 2
          end

          it "does not set the tag name" do
            expect(find_by_consumer_version_number("foo-latest-dev-version").selectors).to eq [ResolvedSelector.new({ latest: true, consumer: "Foo" }, PactBroker::Domain::Version.find(number: "foo-latest-dev-version"))]
            expect(find_by_consumer_version_number("foo-latest-dev-version").overall_latest?).to be true
          end
        end

        context "when two consumers have exactly the same json content" do
          before do
            td.create_consumer
              .create_provider("Bar")
              .create_consumer_version
              .create_pact(json_content: { interactions: ["foo"] }.to_json )
              .create_consumer
              .create_consumer_version
              .create_pact(json_content: { interactions: ["foo"] }.to_json )
          end

          let(:consumer_version_selectors) { Selectors.new }

          it "returns a pact for each consumer" do
            expect(subject.size).to eq 2
          end
        end
      end
    end
  end
end
