#include "gtest/gtest.h"
#include "gmock/gmock.h"
#include <myapp/configuration/configprovider.h>
#include <myapp/configuration/configproviderfactory.h>
#include <stdexcept>
#include <yaml.h>
#include <string>

using namespace ::testing;
using namespace myapp::configuration;
using namespace std::string_literals;

class YamlConfigProviderTests : public Test
{
public:
    std::shared_ptr<config_provider> yaml_cfg = config_provider_factory::create_yaml_config_provider();
};

TEST_F(YamlConfigProviderTests, EmptyDocument_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8""s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, ScalarDocument_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"aaa"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, SequenceDocument_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"- array\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MappingDocument_SequenceAsKey_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"[a, b]: c\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MappingDocument_MappingAsKey_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"? a: b\n: c\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MappingDocument_NullKey_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"?\n: a\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MappingDocument_SequenceAsValue_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"a: [b, c]\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MappingDocument_MappingAsValue_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"? a\n: b: c\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MappingDocument_NullValue_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"? a\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, MultipleDocuments_Throws)
{
    ASSERT_THROW({ yaml_cfg->from_string(u8"a: b\n---\nc: d\n"s); }, bad_document);
}

TEST_F(YamlConfigProviderTests, ReadConfig_RconPathIsValid_ReturnsCorrectConfig)
{
    auto input = u8"remote_console: /var/run/myapp.sock\n"s;
    auto cfg = yaml_cfg->from_string(input);
    ASSERT_THAT(cfg.remote_console_unix_socket_path, StrEq(u8"/var/run/myapp.sock"s));
}

// TEST_F(YamlConfigProviderTests, ReadConfig_RconPathIsRelative_Throws)
// {
//     auto input = u8"remote_console: myapp.sock\n"s;
//     ASSERT_THROW({ yaml_cfg->read_config_from_string(input); }, std::runtime_error);
// }

TEST_F(YamlConfigProviderTests, ReadConfig_SqlitePersistenceConnectionStringIsValid_ReturnsCorrectConfig)
{
    auto input = u8"persistence:\n  sqlite: file:/var/run/myapp.sqlite3\n"s;
    auto cfg = yaml_cfg->from_string(input);
    ASSERT_THAT(cfg.sqlite_persistence_connection_string, StrEq(u8"file:/var/run/myapp.sqlite3"s));
}

TEST_F(YamlConfigProviderTests, ReadConfig_RedisPersistenceConnectionStringIsValid_ReturnsCorrectConfig)
{
    auto input = u8"persistence:\n  redis: 127.0.0.1:6379\n"s;
    auto cfg = yaml_cfg->from_string(input);
    ASSERT_THAT(cfg.redis_persistence_connection_string, StrEq(u8"127.0.0.1:6379"s));
}

TEST_F(YamlConfigProviderTests, Modbus_ReturnsCorrectConfig)
{
	auto input = u8"modbus:\n  'connection_string1':\n    'device1': '1:devicetype1'\n    'device2': '2:devicetype2'";
	auto cfg = yaml_cfg->from_string(input);

	ASSERT_EQ(1, cfg.modbus.size());
	ASSERT_THAT(begin(cfg.modbus)->first, StrEq("connection_string1"s));

	auto devices = begin(cfg.modbus)->second;
	ASSERT_EQ(2, devices.size());

	auto device_i = begin(devices);
	ASSERT_THAT(device_i->first, StrEq("device1"s));
	ASSERT_THAT(device_i->second, StrEq("1:devicetype1"s));

	++device_i;
	ASSERT_THAT(device_i->first, StrEq("device2"s));
	ASSERT_THAT(device_i->second, StrEq("2:devicetype2"s));
}
