#include "../include/logger.h"

#include <gmock/gmock.h>
#include <gtest/gtest.h>

// Mock structures and functions for testing
struct MockLogLine {
  std::string thread_id;
  std::string message;
  bool destroyed = false;
};

// Global mock data for testing
static std::vector<std::tuple<std::string, std::string>> processed_logs;

// Mock implementation of leveldb_rs_log_line_t functions
extern "C" {
leveldb_rs_log_line_t* leveldb_rs_logline_create(void* rust_logger) { // NOLINT(modernize-use-trailing-return-type)
  auto mock_line = std::make_unique<MockLogLine>();
  return reinterpret_cast<leveldb_rs_log_line_t*>(mock_line.release());
}

void leveldb_rs_logline_destroy(leveldb_rs_log_line_t* logline) {
  if (logline != nullptr) {
    std::unique_ptr<MockLogLine> mock_line(reinterpret_cast<MockLogLine*>(logline));
  }
}

void leveldb_rs_logline_process(leveldb_rs_log_line_t* logline, const char* thread_id, const char* message) {
  if (logline != nullptr && thread_id != nullptr && message != nullptr) {
    processed_logs.emplace_back(std::string(thread_id), std::string(message));
  }
}
}

namespace leveldb_rs {

// Test fixture for logger tests
class LoggerTest : public ::testing::Test {
  protected:
  void SetUp() override { processed_logs.clear(); }

  void TearDown() override { processed_logs.clear(); }

  // Helper function to create a logger for testing
  auto create_test_logger() -> leveldb_logger_t* {
    void* fake_rust_logger = reinterpret_cast<void*>(0x12345678);
    return leveldb_rs_logger_create(fake_rust_logger);
  }

  // Helper to call Logv method with format string
  void call_logv(RsLogger* rs_logger, const char* format, ...) {
    va_list args;
    va_start(args, format);
    rs_logger->Logv(format, args);
    va_end(args);
  }
};

// Test logger creation and destruction
TEST_F(LoggerTest, LoggerLifecycle) {
  auto logger = create_test_logger();
  ASSERT_NE(logger, nullptr);

  leveldb_rs_logger_destroy(logger);
}

// Test parameters for message formatting tests
struct MessageFormattingTestParam {
  std::string test_name;
  size_t message_size;
};

class MessageFormattingTest : public LoggerTest, public ::testing::WithParamInterface<MessageFormattingTestParam> {};

// Test message formatting with different message sizes
TEST_P(MessageFormattingTest, MessageFormatting) {
  auto logger = create_test_logger();
  ASSERT_NE(logger, nullptr);
  auto rs_logger = static_cast<RsLogger*>(logger->rep);

  // Create message with specified size
  size_t msg_size = GetParam().message_size;
  std::string msg(msg_size, 'A');
  call_logv(rs_logger, msg.c_str());

  ASSERT_EQ(processed_logs.size(), 1);
  auto [thread_id, message] = processed_logs[0];

  // thread_id is shorter than limit and non-empty
  EXPECT_LE(thread_id.length(), kMaxThreadIdSize);
  EXPECT_FALSE(thread_id.empty());
  // message contains input
  EXPECT_TRUE(message.find("AAAAA") != std::string::npos);
  EXPECT_EQ(message.size(), msg_size);

  leveldb_rs_logger_destroy(logger);
}

// Instantiate test cases for message formatting
INSTANTIATE_TEST_SUITE_P(
    MessageFormattingTests, MessageFormattingTest,
    ::testing::Values(MessageFormattingTestParam{"MessageOfSizeLessThanThreshold", kStackBufferSize - 100},
                      MessageFormattingTestParam{"MessageOfSizeAsThreshold", kStackBufferSize},
                      MessageFormattingTestParam{"MessageOfSizeExceedingThreshold", kStackBufferSize + 100}),
    [](const testing::TestParamInfo<MessageFormattingTestParam>& info) { return info.param.test_name; });

} // namespace leveldb_rs

auto main(int argc, char** argv) -> int {
  ::testing::InitGoogleTest(&argc, argv);
  return RUN_ALL_TESTS();
}
