# frozen_string_literal: true

RSpec.describe Dry::Types::Nominal, "#maybe", :maybe do
  context "with a nominal" do
    subject(:type) { Dry::Types["nominal.string"].maybe }

    it_behaves_like "Dry::Types::Nominal without primitive"

    it "returns None when value is nil" do
      expect(type[nil]).to be_none
    end

    it "returns Some when value exists" do
      expect(type["hello"].value!).to eql("hello")
    end

    it "returns original if input is already a maybe" do
      expect(type[Dry::Monads::Maybe::Some.new("hello")].value!).to eql("hello")
    end

    it "aliases #[] as #call" do
      expect(type.call("hello").value!).to eql("hello")
    end

    it "does not have primitive" do
      expect(type).to_not respond_to(:primitive)
    end
  end

  context "with a strict type" do
    subject(:type) { Dry::Types["strict.integer"].maybe }

    it_behaves_like "Dry::Types::Nominal without primitive"

    it "returns None when value is nil" do
      expect(type[nil]).to be_none
    end

    it "returns Some when value exists" do
      expect(type[231].value!).to be(231)
    end

    it "returns original if input is already a maybe" do
      expect(type[Dry::Monads::Maybe.lift(231)].value!).to be(231)
    end
  end

  context "with a sum" do
    subject(:type) { Dry::Types["nominal.bool"].maybe }

    it_behaves_like "Dry::Types::Nominal without primitive"

    it "returns None when value is nil" do
      expect(type[nil]).to be_none
    end

    it "returns Some when value exists" do
      expect(type[true].value!).to be(true)
      expect(type[false].value!).to be(false)
    end

    it "does not have primitive" do
      expect(type).to_not respond_to(:primitive)
    end
  end

  context "with keys" do
    subject(:type) do
      Dry::Types["hash"].schema(foo: Dry::Types["integer"]).key(:foo)
    end

    it "gets wrapped by key type" do
      expect(type.maybe).to be_a(Dry::Types::Schema::Key)
      expect(type.maybe[nil]).to be_none
      expect(type.maybe[1]).to be_some
    end
  end

  describe "#try" do
    before do
      require "dry/monads"
      extend Dry::Monads[:maybe]
    end

    subject(:type) { Dry::Types["coercible.integer"].maybe }

    it "maps successful result" do
      expect(type.try("1")).to eql(Dry::Types::Result::Success.new(Some(1)))
      expect(type.try(nil)).to eql(Dry::Types::Result::Success.new(None()))
      expect(type.try("a")).to be_a(Dry::Types::Result::Failure)
    end
  end

  describe "#call" do
    describe "safe calls" do
      before do
        require "dry/monads"
        extend Dry::Monads[:maybe]
      end

      subject(:type) { Dry::Types["coercible.integer"].maybe }

      specify do
        expect(type.("a") { :fallback }).to be(:fallback)
        expect(type.(Some(1)) { :fallback }).to eql(Some(1))
      end
    end
  end
end
