# frozen_string_literal: true

RSpec.describe Fear::Extractor::ExtractorMatcher do
  let(:parser) { Fear::Extractor::GrammarParser.new }
  let(:matcher) { parser.parse(pattern).to_matcher }

  describe "#defined_at?" do
    subject { matcher }

    context "boolean extractor" do
      let(:pattern) { "IsEven()" }

      it { is_expected.to be_defined_at(42) }
      it { is_expected.not_to be_defined_at(43) }
      it { is_expected.not_to be_defined_at("foo") }
    end

    context "single argument extractor" do
      let(:pattern) { "Fear::Some(a : Integer)" }

      it { is_expected.to be_defined_at(Fear.some(42)) }
      it { is_expected.not_to be_defined_at("foo") }
      it { is_expected.not_to be_defined_at(Fear.some("foo")) }
    end

    context "single argument extractor with array as an argument" do
      let(:pattern) { "Fear::Some([1, 2])" }

      it { is_expected.to be_defined_at(Fear.some([1, 2])) }
      it { is_expected.not_to be_defined_at(Fear.some([1, 1])) }
      it { is_expected.not_to be_defined_at(Fear.some("foo")) }
    end

    context "multiple arguments extractor" do
      let(:pattern) { "Date(2017, month, _)" }

      it { is_expected.to be_defined_at(Date.parse("2017-02-15")) }
      it { is_expected.not_to be_defined_at(Date.parse("2018-02-15")) }
      it { is_expected.not_to be_defined_at("foo") }
    end

    context "Struct" do
      StructDate = ::Struct.new(:year, :month, :day)

      let(:pattern) { "StructDate(2017, month, _)" }

      it { is_expected.to be_defined_at(StructDate.new(2017, 2, 15)) }
      it { is_expected.not_to be_defined_at(StructDate.new(2018, 2, 15)) }
    end
  end

  describe "#call" do
    subject { matcher.(other) }

    context "single argument extractor" do
      let(:pattern) { "Fear::Some(a : Integer)" }
      let(:other) { Fear.some(42) }

      it { is_expected.to eq(a: 42) }
    end

    context "multiple arguments extractor" do
      let(:pattern) { "Date(2017, month, day)" }
      let(:other) { Date.parse("2017-02-15") }

      it { is_expected.to eq(month: 2, day: 15) }
    end
  end

  describe "#failure_reason" do
    subject { matcher.failure_reason(other) }

    context "no argument extractor" do
      let(:pattern) { "IsEven()" }

      context "defined" do
        let(:other) { 42 }

        it { is_expected.to eq(Fear.none) }
      end

      context "not defined" do
        let(:other) { 43 }

        it { is_expected.to eq(Fear.some(<<~MSG.strip)) }
          Expected `43` to match:
          IsEven()
          ^
        MSG
      end
    end

    context "single argument extractor" do
      let(:pattern) { "Fear::Some(a : Integer)" }

      context "defined" do
        let(:other) { Fear.some(42) }

        it { is_expected.to eq(Fear.none) }
      end

      context "not defined" do
        let(:other) { Fear.some("42") }

        it { is_expected.to eq(Fear.some(<<~MSG.strip)) }
          Expected `"42"` to match:
          Fear::Some(a : Integer)
          ~~~~~~~~~~~~~~~^
        MSG
      end
    end

    context "single argument extractor, array argument" do
      let(:pattern) { "Fear::Some([1, 2])" }

      context "defined" do
        let(:other) { Fear.some([1, 2]) }

        it { is_expected.to eq(Fear.none) }
      end

      context "not defined" do
        let(:other) { Fear.some([1, 1]) }

        it { is_expected.to eq(Fear.some(<<~MSG.strip)) }
          Expected `1` to match:
          Fear::Some([1, 2])
          ~~~~~~~~~~~~~~~^
        MSG
      end
    end

    context "multiple arguments extractor" do
      let(:pattern) { "Date(year, 02, day)" }

      context "defined" do
        let(:other) { Date.parse("2017-02-15") }

        it { is_expected.to eq(Fear.none) }
      end

      context "not defined" do
        let(:other) { Date.parse("2017-04-15") }

        it { is_expected.to eq(Fear.some(<<~MSG.strip)) }
          Expected `4` to match:
          Date(year, 02, day)
          ~~~~~~~~~~~^
        MSG
      end
    end
  end
end