# frozen_string_literal: true

require "assert"
require "much-result/aggregate"

class MuchResult::Aggregate
  class UnitTests < Assert::Context
    desc "MuchResult::Aggregate"
    subject{ unit_class }

    let(:unit_class){ MuchResult::Aggregate }

    let(:values1) do
      [Factory.value, Factory.value, Factory.value, {}]
    end
    let(:hash_values1) do
      [Factory.hash_value, Factory.hash_value, Factory.hash_value]
    end

    should have_imeths :call

    should "build instances and call them" do
      Assert.stub_tap_on_call(subject, :new) do |instance, call|
        @instance_new_call = call
        Assert.stub(instance, :call){ @instance_called = true }
      end

      subject.call(values1)

      assert_that(@instance_new_call.args).equals([values1])
      assert_that(@instance_called).is_true
    end
  end

  class InitTests < UnitTests
    desc "when init"
    subject{ unit_class.new(init_values) }

    let(:init_values){ [values1, hash_values1, [], nil].sample }

    should have_imeths :call
  end

  class CalledWithAnEmptyArrayTests < InitTests
    desc "and called with an empty Array"

    let(:init_values){ [] }
    # let(:init_values) { [[], nil, [nil, nil]].sample }

    should "return an empty Array" do
      assert_that(subject.call).equals([])
    end
  end

  class CalledWithASingleNonHashValueTests < InitTests
    desc "and called with single non-Hash value"

    let(:init_values){ [Factory.value, nil].sample }

    should "return the value wrapped in an Array" do
      assert_that(subject.call).equals([init_values])
    end
  end

  class CalledWithASingleHashValueTests < InitTests
    desc "and called with single Hash value"

    let(:value1){ [Factory.value, nil].sample }
    let(:init_values){ { value: value1 } }

    should "return the Hash with its values wrapped in an Array" do
      assert_that(subject.call).equals(value: [value1])
    end
  end

  class CalledWithAMixedValueArrayTests < InitTests
    desc "and called with a mixed-value Array"

    let(:init_values){ [nil, values1, [], { value: 1 }, nil] }

    should "combines the values into an Array, flattening any sub-Arrays" do
      assert_that(subject.call).equals([nil, *values1, { value: 1 }, nil])
    end
  end

  class CalledWithAnAllHashValueArrayTests < InitTests
    desc "and called with an all-Hash-value Array"

    let(:init_values){ [nil] + hash_values1 + [nil] }

    let(:expected_aggregate_value) do
      {
        value1: init_values.map{ |hash| (hash || {})[:value1] },
        value2: {
          value1: init_values.map{ |hash| (hash || {}).dig(:value2, :value1) },
          value2: init_values.map{ |hash| (hash || {}).dig(:value2, :value2) },
        },
      }
    end

    should "recursively combine the hash values, removing any nils" do
      assert_that(subject.call).equals(expected_aggregate_value)
    end
  end
end
