/*
 * Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
 * MindIE is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *          http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */
#include <gtest/gtest.h>
#include <mockcpp/mockcpp.hpp>
#define private public
#include "event_dispatcher.h"

using namespace mindie_llm;

class EventDispatcherTest : public testing::Test {
protected:
    void SetUp() override
    {
        dispatcher = std::make_shared<EventDispatcher>(123, "test-request", 3);
        sink.write = [&](const char* data, size_t size) -> bool {
            std::cout << "write called with size: " << size << std::endl;
            received_data.append(data, size);
            return true;
        };
        sink.done = [&]() {
            done_called = true;
            std::cout << "done called" << std::endl;
        };
    }

    void TearDown() override
    {
        received_data = "";
        done_called = false;
        GlobalMockObject::verify();
    }

    std::shared_ptr<EventDispatcher> dispatcher;
    httplib::DataSink sink;
    std::string received_data;
    bool done_called = false;

    boost::cv_status (boost::condition_variable::*wait_until_ptr)(
            boost::unique_lock<boost::mutex>&,
            const boost::chrono::steady_clock::time_point&
        ) = &boost::condition_variable::wait_until;
    
};

TEST_F(EventDispatcherTest, NormalEventFlow)
{
    MOCKER_CPP(wait_until_ptr, boost::cv_status (*)(
            boost::unique_lock<boost::mutex>&,
            const boost::chrono::steady_clock::time_point&
        )).stubs().will(MOCKCPP_NS::returnValue(boost::cv_status::no_timeout));
    boost::thread wait_thread([&] {
        boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
        dispatcher->WaitEvent(&sink);
    });
    
    dispatcher->SendEvent("Hello ", false);
    dispatcher->SendEvent("World", true);
    
    wait_thread.join();
    EXPECT_EQ(received_data, "Hello World");
    EXPECT_TRUE(dispatcher->isFinish_.load());
}

TEST_F(EventDispatcherTest, TimeoutScenario)
{
    MOCKER_CPP(wait_until_ptr, boost::cv_status (*)(
            boost::unique_lock<boost::mutex>&,
            const boost::chrono::steady_clock::time_point&
        )).stubs().will(MOCKCPP_NS::returnValue(boost::cv_status::timeout));
    boost::thread wait_thread([&] {
        dispatcher->WaitEvent(&sink);
    });
    
    wait_thread.join();
    
    EXPECT_EQ(received_data, "Engine callback timeout.");
    EXPECT_TRUE(done_called);
    EXPECT_TRUE(dispatcher->isFinish_.load());
}

TEST_F(EventDispatcherTest, ClearFunctionality)
{
    boost::thread wait_thread([&] {
        dispatcher->WaitEvent(&sink);
    });
    
    dispatcher->SendEvent("Test data", false);
    
    while (dispatcher->sendCount_.load() == 0) {
        boost::this_thread::sleep_for(boost::chrono::milliseconds(10));
    }
    
    dispatcher->Clear();
    
    
    dispatcher->SendEvent("", true);
    wait_thread.join();
    EXPECT_EQ(dispatcher->clearCount_.load(), 0);
}

TEST_F(EventDispatcherTest, LastMessageHandling)
{
    MOCKER_CPP(wait_until_ptr, boost::cv_status (*)(
            boost::unique_lock<boost::mutex>&,
            const boost::chrono::steady_clock::time_point&
        )).stubs().will(MOCKCPP_NS::returnValue(boost::cv_status::no_timeout));
    boost::thread wait_thread([&] {
        boost::this_thread::sleep_for(boost::chrono::milliseconds(500));
        dispatcher->WaitEvent(&sink);
    });
    
    dispatcher->SendEvent("Main message", true, " Last part");
    
    wait_thread.join();
    
    EXPECT_EQ(received_data, "Main message Last part");
    EXPECT_TRUE(done_called);
}

TEST_F(EventDispatcherTest, LogAndRequestIds)
{
    EXPECT_EQ(dispatcher->logId, 123);
    EXPECT_EQ(dispatcher->requestId_, "test-request");
}