# frozen_string_literal: true

require "dry/monads"

RSpec.describe Dry::Matcher do
  context "with match cases provided" do
    let(:success_case) do
      Dry::Matcher::Case.new(
        match: -> result { result.success? },
        resolve: -> result { result.value! }
      )
    end

    let(:failure_case) do
      Dry::Matcher::Case.new(
        match: -> result { result.failure? },
        resolve: -> result { result.failure }
      )
    end

    let(:matcher) do
      Dry::Matcher.new(
        success: success_case,
        failure: failure_case
      )
    end

    def call_match(input)
      matcher.(input) do |m|
        m.success do |v|
          "Success: #{v}"
        end

        m.failure do |v|
          "Failure: #{v}"
        end
      end
    end

    it "matches on success" do
      input = Dry::Monads::Success("Yes!")
      expect(call_match(input)).to eq "Success: Yes!"
    end

    it "matches on failure" do
      input = Dry::Monads::Failure("No!")
      expect(call_match(input)).to eq "Failure: No!"
    end

    it "requires an exhaustive match" do
      input = Dry::Monads::Success("Yes!")

      expect {
        matcher.(input) do |m|
          m.success { |v| "Success: #{v}" }
        end
      }.to raise_error Dry::Matcher::NonExhaustiveMatchError
    end

    context "with patterns" do
      let(:success_case) do
        Dry::Matcher::Case.new(
          match: -> result { result.first == :ok },
          resolve: -> result { result.last }
        )
      end

      let(:failure_case) do
        Dry::Matcher::Case.new(
          match: -> result, failure_type {
            result.length == 3 && result[0] == :failure && result[1] == failure_type
          },
          resolve: -> result { result.last }
        )
      end

      def call_match(input)
        matcher.(input) do |m|
          m.success

          m.failure :my_error do |v|
            "Pattern-matched failure: #{v}"
          end
        end
      end

      it "matches using the provided pattern" do
        input = [:failure, :my_error, "No!"]
        expect(call_match(input)).to eq "Pattern-matched failure: No!"
      end

      it "doesn't match if the pattern doesn't match" do
        input = [:failure, :non_matching_error, "No!"]
        expect(call_match(input)).to be_nil
      end
    end
  end
end
