# frozen_string_literal: true

require "dry/events/publisher"

RSpec.describe Dry::Events::Publisher do
  subject(:publisher) do
    Class.new {
      include Dry::Events::Publisher[:test_publisher]

      register_event :test_event
    }.new
  end

  describe ".[]" do
    it "creates a publisher extension with provided id" do
      publisher = Class.new do
        include Dry::Events::Publisher[:my_publisher]
      end

      expect(Dry::Events::Publisher.registry[:my_publisher]).to be(publisher)
    end

    it "does not allow same id to be used for than once" do
      create_publisher = -> do
        Class.new do
          include Dry::Events::Publisher[:my_publisher]
        end
      end

      create_publisher.()

      expect { create_publisher.() }.to raise_error(Dry::Events::PublisherAlreadyRegisteredError, /my_publisher/)
    end
  end

  describe ".subscribe" do
    it "subscribes a listener at class level" do
      listener = -> * {}

      publisher.class.subscribe(:test_event, &listener)

      expect(publisher.subscribed?(listener)).to be(true)
    end

    it "raises an exception when subscribing to an unregister event" do
      listener = -> * {}

      expect {
        publisher.subscribe(:not_register, &listener)
      }.to raise_error(Dry::Events::InvalidSubscriberError, /not_register/)
    end
  end

  describe "#register_event" do
    it "registers a new event at instance level" do
      listener = -> * {}

      publisher.register_event(:test_another_event).subscribe(:test_another_event, &listener)

      expect(publisher.subscribed?(listener)).to be(true)
    end
  end

  describe "#subscribe" do
    it "subscribes a listener function" do
      listener = -> * {}

      publisher.subscribe(:test_event, &listener)

      expect(publisher.subscribed?(listener)).to be(true)
    end

    it "subscribes a listener object" do
      listener = Class.new do
        attr_reader :captured

        def initialize
          @captured = []
        end

        def on_test_event(event)
          captured << event[:message]
        end
      end.new

      publisher.subscribe(listener).publish(:test_event, message: "it works")

      expect(listener.captured).to eql(["it works"])

      publisher.unsubscribe(listener)

      publisher.publish(:test_event, message: "it works")

      expect(listener.captured).to eql(["it works"])
    end

    it "raises an exception when subscribing with no methods to execute" do
      listener = Object.new

      expect {
        publisher.subscribe(listener)
      }.to raise_error(Dry::Events::InvalidSubscriberError, /never be executed/)
    end

    it "does not raise an exception when subscriber has methods for notification" do
      listener = Object.new
      def listener.on_test_event;
        nil;
      end
      expect { publisher.subscribe(listener) }.not_to raise_error
    end
  end

  describe "#unsubscribe" do
    it "unsubscribes a listener" do
      listener = Class.new do
        attr_reader :captured

        def initialize
          @captured = []
        end

        def on_test_event(event)
          captured << event[:message]
        end
      end.new

      captured_by_block = []

      publisher.subscribe(:test_event) do |event|
        captured_by_block << event[:message]
      end
      publisher.subscribe(listener)

      publisher.publish(:test_event, message: "it works")

      expect(listener.captured).to eql(["it works"])
      expect(captured_by_block).to eql(["it works"])

      publisher.unsubscribe(listener)

      publisher.publish(:test_event, message: "it works")

      expect(listener.captured).to eql(["it works"])
      expect(captured_by_block).to eql(["it works", "it works"])
    end
  end

  describe "#publish" do
    it "publishes an event" do
      result = []
      listener = -> event { result << event[:message] }

      publisher.subscribe(:test_event, &listener).publish(:test_event, message: "it works")

      expect(result).to eql(["it works"])
    end

    it "publishes an event filtered by a query" do
      result = []
      listener = -> e { result << e[:test] }

      publisher
        .subscribe(:test_event, test: true, &listener)
        .publish(:test_event, test: false)
        .publish(:test_event, test: true)

      expect(result).to eql([true])
    end

    it "raises an exception when publishing an unregistered event" do
      expect {
        publisher.publish(:unregistered_event, {})
      }.to raise_error(Dry::Events::UnregisteredEventError, /unregistered_event/)
    end
  end

  describe "#process" do
    it "yields event and its listeners" do
      result = []
      listener = -> event { result << event.id }

      publisher.subscribe(:test_event, &listener)

      publisher.process(:test_event) do |event, list|
        list.(event)
      end

      expect(result).to eql([:test_event])
    end
  end

  describe "#subscribed?" do
    it "returns true when checking for previously subscribed instance methods" do
      listener = Object.new
      def listener.on_test_event
        nil
      end

      def listener.on_another_test_event
        nil
      end
      subject.register_event(:another_test_event)
      subject.subscribe(listener)
      expect(publisher.subscribed?(listener.method(:on_test_event))).to be(true)
      expect(publisher.subscribed?(listener.method(:on_another_test_event))).to be(true)
    end
  end
end
