// Copyright 2023 Northern.tech AS
//
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
//
//        http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.

#include <artifact/v3/manifest/manifest.hpp>

#include <string>

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

#include <common/log.hpp>

using namespace std;

TEST(ParserTest, TestParseManifest) {
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  data/0000.tar
9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50  header.tar
96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b  version
)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	ASSERT_TRUE(manifest) << "error message: " << manifest.error().message;

	auto manifest_unwrapped = manifest.value();

	EXPECT_EQ(
		manifest_unwrapped.Get("version"),
		"96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b")
		<< "ONE";
	EXPECT_EQ(
		manifest_unwrapped.Get("header.tar"),
		"9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50")
		<< "TWO";
	EXPECT_EQ(
		manifest_unwrapped.Get("data/0000.tar"),
		"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f")
		<< "THREE";
	EXPECT_EQ(manifest_unwrapped.Get("IDoNotExist"), "");

	// Check the checksum of the whole manifest
	EXPECT_EQ(
		manifest_unwrapped.shasum.String(),
		"cbea329fa8ae6223656b8c96015c41313cd6e7a199400ea6854b0a653052802d")
		<< "SHASUM";
}

TEST(ParserTest, TestParseManifestFormatErrorShasumLength) {
	/* Two characters missing from the shasum */
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb001  data/0000.tar
)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	EXPECT_FALSE(manifest) << manifest.error().message;

	EXPECT_EQ(
		manifest.error().message,
		"Line (aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb001  data/0000.tar) is not in the expected manifest format: ^([0-9a-z]{64})[[:space:]]{2}([^[:blank:]]+)$");
}

TEST(ParserTest, TestParseManifestFormatErrorMissingName) {
	std::string manifest_data =
		R"(96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b
)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	EXPECT_FALSE(manifest) << manifest.error().message;

	EXPECT_EQ(
		manifest.error().message,
		"Line (96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b) is not in the expected manifest format: ^([0-9a-z]{64})[[:space:]]{2}([^[:blank:]]+)$");
}


TEST(ParserTest, TestParseManifestFormatErrorWrongNumberOfWhitespaceSeparators) {
	/* 3 instead of two spaces in between the sha and the name */
	std::string manifest_data =
		R"(96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b   version
)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	EXPECT_FALSE(manifest);

	EXPECT_EQ(
		manifest.error().message,
		"Line (96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b   version) is not in the expected manifest format: ^([0-9a-z]{64})[[:space:]]{2}([^[:blank:]]+)$");
}

TEST(ParserTest, TestParseManifestFormatErrorAllOnOneLine) {
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  data/00 00.tar
9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50  header.tar
96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b  version)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	EXPECT_FALSE(manifest);

	EXPECT_EQ(
		manifest.error().message,
		"Line (aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  data/00 00.tar) is not in the expected manifest format: ^([0-9a-z]{64})[[:space:]]{2}([^[:blank:]]+)$");
}

TEST(ParserTest, TestParseManifestFormatErrorNewlineSeparators) {
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f data/0000.tar 9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50 header.tar)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	EXPECT_FALSE(manifest) << manifest.error().message << std::endl;

	EXPECT_EQ(
		manifest.error().message,
		"Line (aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f data/0000.tar 9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50 header.tar) is not in the expected manifest format: ^([0-9a-z]{64})[[:space:]]{2}([^[:blank:]]+)$");
}


TEST(ParserTest, TestParseManifestFormatStripHeaderCompressionSuffixes) {
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  data/0000/header.tar.xz
38af7c0bb65d4a0249683e9534e5292f236f59b9d523d9e1ca43cabae5acf716  data/0000/header.tar.zst
bb0b80f5c2bc051db240c5fa356fcc249a596e6380b27fb53753ff26ae505c9b  data/0000/header.tar.xx
aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  manifest
9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50  header.tar.gz
5fc410da6093c52dae7db42e68ef36e639c380a96e2783fc7c56eb0c95dcc8e2  header.tar.xx
81c7420e3ace324f67a1ae15e15edc4909e4dd448a2c503e09bf5dd24d2dcc54  header-augment.tar.gz
2c71b37b51581078d41cc7749885eaa5b01ef78a2f62355015c582dc085adca2  header-augment.tar.xx
96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b  version
)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	ASSERT_TRUE(manifest) << "error message: " << manifest.error().message;

	auto manifest_unwrapped = manifest.value();

	ASSERT_EQ(
		manifest_unwrapped.Get("data/0000/header.tar.xz"),
		"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f");
	ASSERT_EQ(
		manifest_unwrapped.Get("data/0000/header.tar.xx"),
		"bb0b80f5c2bc051db240c5fa356fcc249a596e6380b27fb53753ff26ae505c9b");
	ASSERT_EQ(
		manifest_unwrapped.Get("data/0000/header.tar.zst"),
		"38af7c0bb65d4a0249683e9534e5292f236f59b9d523d9e1ca43cabae5acf716");
	ASSERT_EQ(manifest_unwrapped.Get("data/0000/header.tar"), "");

	ASSERT_EQ(
		manifest_unwrapped.Get("manifest"),
		"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f");

	ASSERT_EQ(
		manifest_unwrapped.Get("header.tar"),
		"9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50");
	ASSERT_EQ(manifest_unwrapped.Get("header.tar.gz"), "");

	ASSERT_EQ(
		manifest_unwrapped.Get("header-augment.tar"),
		"81c7420e3ace324f67a1ae15e15edc4909e4dd448a2c503e09bf5dd24d2dcc54");
	ASSERT_EQ(manifest_unwrapped.Get("header-augment.tar.gz"), "");
	ASSERT_EQ(
		manifest_unwrapped.Get("header-augment.tar.xx"),
		"2c71b37b51581078d41cc7749885eaa5b01ef78a2f62355015c582dc085adca2");
}

TEST(ParserTest, TestParseManifestShortFilenames) {
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  a
9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50  bb
96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b  ccc
)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto manifest = mender::artifact::v3::manifest::Parse(sr);

	ASSERT_TRUE(manifest) << "error message: " << manifest.error().message;

	auto manifest_unwrapped = manifest.value();

	EXPECT_EQ(
		manifest_unwrapped.Get("a"),
		"aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f");
	EXPECT_EQ(
		manifest_unwrapped.Get("bb"),
		"9f65db081a46f7832b9767c56afcc7bfe784f0a62cc2950b6375b2b6390e6e50");
	EXPECT_EQ(
		manifest_unwrapped.Get("ccc"),
		"96bcd965947569404798bcbdb614f103db5a004eb6e364cfc162c146890ea35b");
}

TEST(ParserTest, TestParseManifestLongFilenames) {
	std::string long_filename(100, '*');
	std::string manifest_data =
		R"(aec070645fe53ee3b3763059376134f058cc337247c978add178b6ccdfb0019f  )" + long_filename
		+ R"(1)";

	std::stringstream ss {manifest_data};

	mender::common::io::StreamReader sr {ss};

	auto expected_manifest = mender::artifact::v3::manifest::Parse(sr);

	ASSERT_FALSE(expected_manifest);

	EXPECT_THAT(
		expected_manifest.error().message,
		testing::HasSubstr("*****1) is too long, maximum allowed filename length is"));
}
