import { IEvent, IRequest } from "@uxland/harmonix";
import { Mock, beforeEach, describe, expect, it, vi } from "vitest";
import { createBroker } from "./factory";
import { PrimariaBroker } from "./primaria-broker";

describe("Broker test", () => {
  let sut: PrimariaBroker;

  beforeEach(() => {
    sut = createBroker();
  });

  describe("Event bus test", () => {
    describe("Test by Event instance", () => {
      class MyEvent implements IEvent {
        constructor(public greeting: string) {}
      }
      describe("Given some subscriptions for an event", () => {
        let handlers: Mock[];
        beforeEach(() => {
          handlers = Array.from({ length: 5 }).map(() => {
            const handler = vi.fn();
            sut.subscribe(MyEvent, handler);
            return handler;
          });
        });
        describe("When event is published", () => {
          beforeEach(() => {
            sut.publish(new MyEvent("Hello"));
          });
          it("Then handler is called", () => {
            for (const handler of handlers) {
              expect(handler).toBeCalledWith({ greeting: "Hello" });
            }
          });
        });
      });
    });
    describe("Test by event name", () => {
      describe("Given some subscriptions for an event", () => {
        let handlers: Mock[];
        beforeEach(() => {
          handlers = Array.from({ length: 5 }).map(() => {
            const handler = vi.fn();
            sut.subscribe("MyEvent", handler);
            return handler;
          });
        });
        describe("When event is published", () => {
          beforeEach(() => {
            sut.publish("MyEvent", { greeting: "Hello" });
          });
          it("Then handler is called", () => {
            for (const handler of handlers) {
              expect(handler).toBeCalledWith({ greeting: "Hello" });
            }
          });
        });
      });
      describe("Given no subscriptions for an event", () => {
        it("When event is published, Then nothing happens", () => {
          expect(() => sut.publish("MyEvent", { greeting: "Hello" })).not.toThrow();
        });
      });
    });
  });
  describe("Message broker test", () => {
    describe("Test by Request instance", () => {
      class MyRequest implements IRequest<string> {
        constructor(public greeting: string) {}
      }
      describe("Given a handler for a request", () => {
        let handler: Mock;
        const greeting = "World!";
        beforeEach(() => {
          handler = vi.fn();
          handler.mockResolvedValue(`Hello ${greeting}`);
          sut.registerRequest(MyRequest, handler);
        });
        describe("When request is sent", () => {
          let result: string;
          beforeEach(async () => {
            result = await sut.send(new MyRequest(greeting));
          });
          it("Then handler is called", () => {
            expect(handler).toBeCalledWith({ greeting });
          });
          it("Then result is returned", () => {
            expect(result).toBe(`Hello ${greeting}`);
          });
        });
      });
      it("Should fail if a request is registered twice", () => {
        sut.registerRequest(MyRequest, () => {});
        expect(() => sut.registerRequest(MyRequest, () => {})).toThrowError();
      });
    });

    describe("Test by request name", () => {
      const requestName = "MyRequest";
      describe("Given a handler for a request", () => {
        let handler: Mock;
        const greeting = "World!";

        beforeEach(() => {
          handler = vi.fn();
          handler.mockResolvedValue(`Hello ${greeting}`);
          sut.registerRequest(requestName, handler);
        });
        describe("When request is sent", () => {
          let result: string;
          beforeEach(async () => {
            result = await sut.send(requestName, { greeting });
          });
          it("Then handler is called", () => {
            expect(handler).toBeCalledWith({ greeting });
          });
          it("Then result is returned", () => {
            expect(result).toBe(`Hello ${greeting}`);
          });
        });
      });
      it("Should fail if a request is registered twice", () => {
        sut.registerRequest(requestName, () => {});
        expect(() => sut.registerRequest(requestName, () => {})).toThrowError();
      });
    });
  });
});
