#include <gtest/gtest.h>
#include "test_access.h"

class BandbridgeCtrlqTest : public ::testing::Test {
protected:
    void SetUp() override
    {
        mock_reset_all();
        test_reset_ctrlq_globals();
        test_set_ctrlq_timeout(2);
        mock_msleep_instant = true;
    }

    void TearDown() override
    {
        mock_msleep_instant = false;
    }
};

static void setup_sq(int depth, void* base_addr)
{
    g_ctrlq_info.sq.depth = depth;
    g_ctrlq_info.sq.base_addr = (void*)base_addr;
    g_ctrlq_info.sq.pi = 0;
    g_ctrlq_info.sq.ci = 0;
}

static void setup_rq(int depth, void* base_addr)
{
    g_ctrlq_info.rq.depth = depth;
    g_ctrlq_info.rq.base_addr = (void*)base_addr;
    g_ctrlq_info.rq.pi = 0;
    g_ctrlq_info.rq.ci = 0;
}

TEST_F(BandbridgeCtrlqTest, GetSqSize_ReturnsCorrectValue)
{
    g_ctrlq_info.sq.depth = 16;
    EXPECT_EQ(bandbridge_ctrlq_get_sq_size(), 16 * CTRLQ_BB_SIZE);
}

TEST_F(BandbridgeCtrlqTest, GetRqSize_ReturnsCorrectValue)
{
    g_ctrlq_info.rq.depth = 16;
    EXPECT_EQ(bandbridge_ctrlq_get_rq_size(), 16 * CTRLQ_BB_SIZE);
}

TEST_F(BandbridgeCtrlqTest, RemainSpace_EmptyQueue_ReturnsDepth)
{
    g_ctrlq_info.sq.depth = 16;
    g_ctrlq_info.sq.pi = 0;
    g_ctrlq_info.sq.ci = 0;
    mock_set_reg(CTRLQ_TX_HEAD_REG, 0);
    EXPECT_EQ(test_ctrlq_remain_space(), 16);
}

TEST_F(BandbridgeCtrlqTest, RemainSpace_HalfFull_ReturnsHalfDepth)
{
    g_ctrlq_info.sq.depth = 16;
    g_ctrlq_info.sq.pi = 8;
    mock_set_reg(CTRLQ_TX_HEAD_REG, 0);
    EXPECT_EQ(test_ctrlq_remain_space(), 8);
}

TEST_F(BandbridgeCtrlqTest, CheckSqEnough_EnoughSpace_ReturnsZero)
{
    g_ctrlq_info.sq.depth = 16;
    g_ctrlq_info.sq.pi = 0;
    mock_set_reg(CTRLQ_TX_HEAD_REG, 0);
    EXPECT_EQ(bandbridge_ctrlq_check_sq_enough(CTRLQ_BB_SIZE), 0);
}

TEST_F(BandbridgeCtrlqTest, CheckSqEnough_NotEnoughSpace_ReturnsNospc)
{
    g_ctrlq_info.sq.depth = 2;
    g_ctrlq_info.sq.pi = 1;
    mock_set_reg(CTRLQ_TX_HEAD_REG, 0);
    EXPECT_EQ(bandbridge_ctrlq_check_sq_enough(3 * CTRLQ_BB_SIZE), -ENOSPC);
}

TEST_F(BandbridgeCtrlqTest, RqIsEmpty_EmptyQueue_ReturnsTrue)
{
    g_ctrlq_info.rq.depth = 16;
    g_ctrlq_info.rq.pi = 0;
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 0);
    EXPECT_EQ(test_ctrlq_rq_is_empty(), 1);
}

TEST_F(BandbridgeCtrlqTest, RqIsEmpty_NotEmpty_ReturnsFalse)
{
    g_ctrlq_info.rq.depth = 16;
    g_ctrlq_info.rq.pi = 0;
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 5);
    EXPECT_EQ(test_ctrlq_rq_is_empty(), 0);
}

TEST_F(BandbridgeCtrlqTest, UpdateRqCi_AdvancesCi)
{
    g_ctrlq_info.rq.depth = 16;
    g_ctrlq_info.rq.ci = 3;
    test_ctrlq_update_rq_ci(4);
    EXPECT_EQ(g_ctrlq_info.rq.ci, 7);
    EXPECT_EQ(mock_get_reg(CTRLQ_RX_HEAD_REG), 7);
}

TEST_F(BandbridgeCtrlqTest, ResetRqCi_SetsCiToPi)
{
    g_ctrlq_info.rq.depth = 16;
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 10);
    test_ctrlq_reset_rq_ci();
    EXPECT_EQ(g_ctrlq_info.rq.ci, 10);
    EXPECT_EQ(mock_get_reg(CTRLQ_RX_HEAD_REG), 10);
}

TEST_F(BandbridgeCtrlqTest, SendToSq_WritesDataAndAdvancesPi)
{
    char* base = (char*)malloc(16 * CTRLQ_BB_SIZE);
    memset(base, 0, 16 * CTRLQ_BB_SIZE);
    setup_sq(16, base);
    mock_set_reg(CTRLQ_TX_TAIL_REG, 0);

    char sendbuf[CTRLQ_BB_SIZE];
    memset(sendbuf, 0xAB, CTRLQ_BB_SIZE);
    bandbridge_ctrlq_send_to_sq(sendbuf, CTRLQ_BB_SIZE);

    EXPECT_EQ(g_ctrlq_info.sq.pi, 1);
    EXPECT_EQ(memcmp(base, sendbuf, CTRLQ_BB_SIZE), 0);

    free(base);
    g_ctrlq_info.sq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, SendToSq_MultiBb_WritesAllAndWraps)
{
    int depth = 4;
    char* base = (char*)malloc(depth * CTRLQ_BB_SIZE);
    memset(base, 0, depth * CTRLQ_BB_SIZE);
    setup_sq(depth, base);
    mock_set_reg(CTRLQ_TX_TAIL_REG, 0);

    char sendbuf[2 * CTRLQ_BB_SIZE];
    memset(sendbuf, 0xCC, 2 * CTRLQ_BB_SIZE);
    bandbridge_ctrlq_send_to_sq(sendbuf, 2 * CTRLQ_BB_SIZE);

    EXPECT_EQ(g_ctrlq_info.sq.pi, 2);
    EXPECT_EQ(memcmp(base, sendbuf, 2 * CTRLQ_BB_SIZE), 0);

    free(base);
    g_ctrlq_info.sq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, ReadDataFromRq_ReadsCorrectData)
{
    int depth = 8;
    char* base = (char*)malloc(depth * CTRLQ_BB_SIZE);
    memset(base, 0, depth * CTRLQ_BB_SIZE);
    setup_rq(depth, base);
    g_ctrlq_info.rq.ci = 3;

    char pattern[CTRLQ_BB_SIZE];
    memset(pattern, 0xDD, CTRLQ_BB_SIZE);
    memcpy(base + 3 * CTRLQ_BB_SIZE, pattern, CTRLQ_BB_SIZE);

    char recvbuf[CTRLQ_BB_SIZE] = {0};
    test_ctrlq_read_data_from_rq(recvbuf, 1);
    EXPECT_EQ(memcmp(recvbuf, pattern, CTRLQ_BB_SIZE), 0);

    free(base);
    g_ctrlq_info.rq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, ReceiveFromRq_Success_ReturnsZero)
{
    int depth = 8;
    char* base = (char*)malloc(depth * CTRLQ_BB_SIZE);
    memset(base, 0, depth * CTRLQ_BB_SIZE);
    setup_rq(depth, base);
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 1);

    write_msg_header(base, 0x0042, 1);

    char recvbuf[2 * CTRLQ_BB_SIZE] = {0};
    int recvbuf_size = 2 * CTRLQ_BB_SIZE;
    int ret = bandbridge_ctrlq_receive_from_rq(recvbuf, &recvbuf_size, 0x0042);
    EXPECT_EQ(ret, 0);

    free(base);
    g_ctrlq_info.rq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, ReceiveFromRq_BbNumZero_ReturnsNospc)
{
    int depth = 8;
    char* base = (char*)malloc(depth * CTRLQ_BB_SIZE);
    memset(base, 0, depth * CTRLQ_BB_SIZE);
    setup_rq(depth, base);
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 1);

    write_msg_header(base, 0x0042, 0);

    char recvbuf[CTRLQ_BB_SIZE] = {0};
    int recvbuf_size = CTRLQ_BB_SIZE;
    EXPECT_EQ(bandbridge_ctrlq_receive_from_rq(recvbuf, &recvbuf_size, 0x0042), -ENOSPC);

    free(base);
    g_ctrlq_info.rq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, ReceiveFromRq_RecvbufTooSmall_ReturnsNospc)
{
    int depth = 8;
    char* base = (char*)malloc(depth * CTRLQ_BB_SIZE);
    memset(base, 0, depth * CTRLQ_BB_SIZE);
    setup_rq(depth, base);
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 1);

    write_msg_header(base, 0x0042, 2);

    char recvbuf[CTRLQ_BB_SIZE] = {0};
    int recvbuf_size = CTRLQ_BB_SIZE;
    EXPECT_EQ(bandbridge_ctrlq_receive_from_rq(recvbuf, &recvbuf_size, 0x0042), -ENOSPC);

    free(base);
    g_ctrlq_info.rq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, ReceiveFromRq_SeqMismatch_SkipsAndTimeout)
{
    int depth = 8;
    char* base = (char*)malloc(depth * CTRLQ_BB_SIZE);
    memset(base, 0, depth * CTRLQ_BB_SIZE);
    setup_rq(depth, base);
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 1);

    write_msg_header(base, 0x0099, 1);

    char recvbuf[CTRLQ_BB_SIZE] = {0};
    int recvbuf_size = CTRLQ_BB_SIZE;
    EXPECT_EQ(bandbridge_ctrlq_receive_from_rq(recvbuf, &recvbuf_size, 0x0042), -ETIMEDOUT);

    free(base);
    g_ctrlq_info.rq.base_addr = NULL;
}

TEST_F(BandbridgeCtrlqTest, ReceiveFromRq_EmptyTimeout_ReturnsTimedout)
{
    setup_rq(8, NULL);
    g_ctrlq_info.rq.ci = 0;
    mock_set_reg(CTRLQ_RX_TAIL_REG, 0);

    char recvbuf[CTRLQ_BB_SIZE] = {0};
    int recvbuf_size = CTRLQ_BB_SIZE;
    EXPECT_EQ(bandbridge_ctrlq_receive_from_rq(recvbuf, &recvbuf_size, 0x0042), -ETIMEDOUT);
}

TEST_F(BandbridgeCtrlqTest, MapReg_IoremapFail_ReturnsNoMem)
{
    mock_set_ioremap_fail(true);
    EXPECT_EQ(test_ctrlq_map_reg(), -ENOMEM);
}

TEST_F(BandbridgeCtrlqTest, MapReg_Success_ReturnsZero)
{
    int ret = test_ctrlq_map_reg();
    EXPECT_EQ(ret, 0);
    EXPECT_NE(g_ctrlq_va, nullptr);
    test_ctrlq_unmap_reg();
}

TEST_F(BandbridgeCtrlqTest, UnmapQueue_ClearsBaseAddr)
{
    mock_set_reg(CTRLQ_TX_DEPTH_REG, 4);
    mock_set_reg(CTRLQ_RX_DEPTH_REG, 4);
    mock_set_reg(CTRLQ_TX_BASEADDR_L_REG, 0x1000);
    mock_set_reg(CTRLQ_TX_BASEADDR_H_REG, 0);
    mock_set_reg(CTRLQ_RX_BASEADDR_L_REG, 0x2000);
    mock_set_reg(CTRLQ_RX_BASEADDR_H_REG, 0);
    EXPECT_EQ(test_ctrlq_map_queue(), 0);

    test_ctrlq_unmap_queue();
    EXPECT_EQ(g_ctrlq_info.sq.base_addr, nullptr);
    EXPECT_EQ(g_ctrlq_info.rq.base_addr, nullptr);
}

TEST_F(BandbridgeCtrlqTest, MapQueue_DepthZero_ReturnsInval)
{
    mock_set_reg(CTRLQ_TX_DEPTH_REG, 0);
    mock_set_reg(CTRLQ_RX_DEPTH_REG, 0);
    EXPECT_EQ(test_ctrlq_map_queue(), -EINVAL);
}

TEST_F(BandbridgeCtrlqTest, Init_Success_ReturnsZero)
{
    mock_set_reg(CTRLQ_TX_DEPTH_REG, 4);
    mock_set_reg(CTRLQ_RX_DEPTH_REG, 4);
    mock_set_reg(CTRLQ_TX_BASEADDR_L_REG, 0x1000);
    mock_set_reg(CTRLQ_TX_BASEADDR_H_REG, 0);
    mock_set_reg(CTRLQ_RX_BASEADDR_L_REG, 0x2000);
    mock_set_reg(CTRLQ_RX_BASEADDR_H_REG, 0);

    int ret = bandbridge_ctrlq_init();
    EXPECT_EQ(ret, 0);
    EXPECT_EQ(g_ctrlq_info.sq.depth, 4 * CTRLQ_DEPTH_UINT);
    EXPECT_NE(g_ctrlq_info.sq.base_addr, nullptr);
    EXPECT_NE(g_ctrlq_info.rq.base_addr, nullptr);

    bandbridge_ctrlq_deinit();
}

TEST_F(BandbridgeCtrlqTest, Init_IoremapFail_RegMapFails)
{
    mock_set_ioremap_fail(true);
    EXPECT_NE(bandbridge_ctrlq_init(), 0);
}