#include <gtest/gtest.h>
#include <thread>
#include "Metadata.h"

/**
 * @file MetadataTest.cpp
 * @brief Unit tests and integration tests for the Metadata class.
 */

/**
 * @brief Test suite for the Metadata class.
 */
class MetadataTest : public ::testing::Test {
protected:
    void SetUp() override {
        // Common setup for tests (if needed)
    }

    void TearDown() override {
        // Clean up after tests (if needed)
    }
};

/**
 * @test Verify default constructor initializes correctly.
 */
TEST_F(MetadataTest, DefaultConstructor) {
    Metadata metadata;

    int expectedByteSize = 0;

    EXPECT_EQ(metadata.getByteSize(), expectedByteSize);
    EXPECT_EQ(metadata.getDataType(), "");
    EXPECT_TRUE(metadata.getDimensions().empty());
    EXPECT_FALSE(metadata.isDebugEnabled());
}

/**
 * @test Verify parameterized constructor initializes correctly.
 */
TEST_F(MetadataTest, ParameterizedConstructor) {
    std::vector<std::size_t> dimensions = {3, 4, 5};
    Metadata metadata(120, "float", dimensions, true);

    int expectedByteSize = 120;
    EXPECT_EQ(metadata.getByteSize(), expectedByteSize);
    EXPECT_EQ(metadata.getDataType(), "float");
    EXPECT_EQ(metadata.getDimensions(), dimensions);
    EXPECT_TRUE(metadata.isDebugEnabled());
}

/**
 * @test Verify setters and getters for byte size.
 */
TEST_F(MetadataTest, ByteSizeSetterGetter) {
    Metadata metadata;
    metadata.setByteSize(256);

    EXPECT_EQ(metadata.getByteSize(), 256);
}

/**
 * @test Verify setters and getters for data type.
 */
TEST_F(MetadataTest, DataTypeSetterGetter) {
    Metadata metadata;
    metadata.setDataType("double");

    EXPECT_EQ(metadata.getDataType(), "double");
}

/**
 * @test Verify setters and getters for dimensions.
 */
TEST_F(MetadataTest, DimensionsSetterGetter) {
    Metadata metadata;
    std::vector<std::size_t> dimensions = {2, 2, 2};
    metadata.setDimensions(dimensions);

    EXPECT_EQ(metadata.getDimensions(), dimensions);
}

/**
 * @test Verify setters and getters for debug flag.
 */
TEST_F(MetadataTest, DebugFlagSetterGetter) {
    Metadata metadata;
    metadata.setDebugEnabled(true);

    EXPECT_TRUE(metadata.isDebugEnabled());

    metadata.setDebugEnabled(false);

    EXPECT_FALSE(metadata.isDebugEnabled());
}

/**
 * @test Verify metadata output with operator<<.
 */
TEST_F(MetadataTest, MetadataOutputOperator) {
    Metadata metadata(64, "int", {8, 8}, true);

    std::ostringstream oss;
    oss << metadata;

    std::string expectedOutput = "Metadata Information:\n"
                                 "  Byte Size: 64 bytes\n"
                                 "  Data Type: int\n"
                                 "  Dimensions: [8, 8]\n"
                                 "  Debug Enabled: Yes\n";

    EXPECT_EQ(oss.str(), expectedOutput);
}

/**
 * @test Integration test: Validate metadata behavior in a multi-step scenario.
 */
TEST_F(MetadataTest, IntegrationScenario) {
    Metadata metadata;

    // Step 1: Initialize metadata with some values
    metadata.setByteSize(128);
    metadata.setDataType("float");
    metadata.setDimensions({4, 4, 2});
    metadata.setDebugEnabled(true);

    EXPECT_EQ(metadata.getByteSize(), 128);
    EXPECT_EQ(metadata.getDataType(), "float");
    EXPECT_EQ(metadata.getDimensions(), (std::vector<std::size_t>{4, 4, 2}));
    EXPECT_TRUE(metadata.isDebugEnabled());

    // Step 2: Simulate a metadata update
    metadata.setByteSize(256);
    metadata.setDataType("double");
    metadata.setDimensions({8, 8});
    metadata.setDebugEnabled(false);

    EXPECT_EQ(metadata.getByteSize(), 256);
    EXPECT_EQ(metadata.getDataType(), "double");
    EXPECT_EQ(metadata.getDimensions(), (std::vector<std::size_t>{8, 8}));
    EXPECT_FALSE(metadata.isDebugEnabled());
}

/**
 * @test Integration test: Handling edge cases for metadata.
 */
TEST_F(MetadataTest, EdgeCaseHandling) {
    Metadata metadata;

    // Case 1: Empty dimensions
    metadata.setDimensions({});
    EXPECT_TRUE(metadata.getDimensions().empty());

    // Case 2: Large byte size
    metadata.setByteSize(static_cast<std::size_t>(-1));
    EXPECT_EQ(metadata.getByteSize(), static_cast<std::size_t>(-1));

    // Case 3: Empty data type
    metadata.setDataType("");
    EXPECT_EQ(metadata.getDataType(), "");
}

