/*
* Author:          Patrick-Christopher Mattulat
* Co-Author:       Claude Sonnet 4.6 (LLM)
* Company:         Lynar Studios
* E-Mail:          webmaster@lynarstudios.com
* Created:         2024-05-16
* Changed:         2026-06-23
*
* */

#include <gtest/gtest.h>
#include <ls-std-event-test.hpp>
#include <ls-std/ls-std-core.hpp>
#include <ls-std/ls-std-event.hpp>
#include <string>

using ls::standard::core::EventNotHandledException;
using ls::standard::event::EventManager;
using ls::standard::event::EventParameter;
using ::std::make_shared;
using ::std::string;
using test::event::Button;
using test::event::ChangeColorEvent;
using test::event::OnClickEvent;
using testing::Test;

namespace
{
  class EventManagerTest : public Test
  {
    public:

      EventManagerTest() = default;
      ~EventManagerTest() override = default;

      static EventParameter generateParameter(const string &_color)
      {
        EventParameter parameter{};
        parameter.addParameter(_color);

        return parameter;
      }
  };

  TEST_F(EventManagerTest, getClassName)
  {
    ASSERT_STREQ("EventManager", EventManager().getClassName().c_str());
  }

  TEST_F(EventManagerTest, getNextProvisionId)
  {
    auto eventManager = EventManager();
    ASSERT_EQ(1, eventManager.getNextProvisionId());
    ASSERT_EQ(1, eventManager.requestListenerId());
    ASSERT_EQ(2, eventManager.getNextProvisionId());
  }

  TEST_F(EventManagerTest, holdsListenerForEvent)
  {
    const auto eventManager = make_shared<EventManager>();
    ASSERT_FALSE(eventManager->holdsListenerForEvent(1, OnClickEvent()));
  }

  TEST_F(EventManagerTest, invoke)
  {
    const auto eventManager = make_shared<EventManager>();

    auto myBlueButton = make_shared<Button>();
    myBlueButton->subscribe(OnClickEvent().supervisedBy(eventManager), [myBlueButton]() mutable { myBlueButton->onClickEvent(); });
    myBlueButton->subscribe(ChangeColorEvent().supervisedBy(eventManager), [myBlueButton]() mutable { myBlueButton->onChangeColorEvent(); });
    const auto myRedButton = make_shared<Button>();
    auto myGreenButton = make_shared<Button>();
    myGreenButton->subscribe(ChangeColorEvent().supervisedBy(eventManager), [myGreenButton]() mutable { myGreenButton->onChangeColorEvent(); });

    // status without any event invoked

    ASSERT_FALSE(myBlueButton->isClicked());
    ASSERT_STREQ("black", myBlueButton->getColor().c_str());
    ASSERT_FALSE(myRedButton->isClicked());
    ASSERT_STREQ("black", myRedButton->getColor().c_str());
    ASSERT_FALSE(myGreenButton->isClicked());
    ASSERT_STREQ("black", myGreenButton->getColor().c_str());

    eventManager->invoke(OnClickEvent());
    eventManager->invoke(ChangeColorEvent(), EventManagerTest::generateParameter("pink"));

    // status with events invoked

    ASSERT_TRUE(myBlueButton->isClicked());
    ASSERT_STREQ("pink", myBlueButton->getColor().c_str());
    ASSERT_FALSE(myRedButton->isClicked());
    ASSERT_STREQ("black", myRedButton->getColor().c_str());
    ASSERT_FALSE(myGreenButton->isClicked());
    ASSERT_STREQ("pink", myGreenButton->getColor().c_str());
  }

  TEST_F(EventManagerTest, invoke_event_not_known)
  {
    const auto eventManager = make_shared<EventManager>();

    EXPECT_THROW(
        {
          try
          {
            eventManager->invoke(OnClickEvent());
          }
          catch (const EventNotHandledException &_exception)
          {
            const string actual = _exception.what();
            const string expected = _exception.getName() + " thrown - event OnClickEvent is not known by event manager";

            EXPECT_STREQ(expected.c_str(), actual.c_str());
            throw;
          }
        },
        EventNotHandledException);
  }

  TEST_F(EventManagerTest, requestListenerId)
  {
    auto eventManager = EventManager();

    ASSERT_EQ(1, eventManager.getNextProvisionId());
    ASSERT_EQ(1, eventManager.requestListenerId());
    ASSERT_EQ(2, eventManager.getNextProvisionId());
  }

  TEST_F(EventManagerTest, subscribeListenerForEvent)
  {
    const auto eventManager = make_shared<EventManager>();
    auto myButton = make_shared<Button>();

    eventManager->subscribeListenerForEvent(myButton, OnClickEvent(), [myButton]() mutable { myButton->onClickEvent(); });
    ASSERT_TRUE(eventManager->holdsListenerForEvent(myButton->getId(), OnClickEvent()));
  }

  TEST_F(EventManagerTest, unsubscribeListenerForEvent)
  {
    const auto eventManager = make_shared<EventManager>();
    auto myButton = make_shared<Button>();

    eventManager->subscribeListenerForEvent(myButton, OnClickEvent(), [myButton]() mutable { myButton->onClickEvent(); });
    ASSERT_TRUE(eventManager->holdsListenerForEvent(myButton->getId(), OnClickEvent()));
    eventManager->unsubscribeListenerForEvent(myButton, OnClickEvent());
    ASSERT_FALSE(eventManager->holdsListenerForEvent(myButton->getId(), OnClickEvent()));
  }
}
