// Unit Tests for LoadPackage*Jobs
// SPDX-FileCopyrightText: 2024 <A Schenck> <galiven@users.sourceforge.net>
// SPDX-License-Identifier: GPL-2.0-only OR GPL-3.0-only OR LicenseRef-KDE-Accepted-GPL

#undef QT_NO_CAST_FROM_ASCII
#include "testhelpers.hpp"
#include "loadpackagefilestests.h"

#include <QList>
#include <QString>
#include <QTest>
#include "fakeit.hpp"
using namespace fakeit;
//#include "trompeloeil-49/trompeloeil.hpp"

#include <ThreadWeaver/Job>
#include <ThreadWeaver/Thread>

#include "config/configdialog.h"
#include "core/LoadPackageHardMaskJob.h"
#include "core/LoadPackageKeywordsJob.h"

QTEST_APPLESS_MAIN(LoadPackageFilesTests);

void LoadPackageFilesTests::initTestCase() {
	// Called before the first testfunction is executed
	TestHelpers::initTestDir();
}

void LoadPackageFilesTests::cleanupTestCase() {
	// Called after the last testfunction was executed
	TestHelpers::cleanupTestDir();
}

void LoadPackageFilesTests::init() {
	// Called before each testfunction is executed
}

void LoadPackageFilesTests::cleanup() {
	// Called after every testfunction
}

//Made it work with Trompeloeil first because fakeit.hpp has a build error with clang 19
//and some warnings, but it requires this giant amount of boilerplate. So after looking
//back at FakeIt and seeing https://github.com/eranpeer/FakeIt/issues/348 which is the
//same compile error with an easy workaround it seemed sensible to retry FakeIt which
//isn't perfect but still feels better than this.
// class MockDbConnection : public DbConnection {
// public:
// 	MockDbConnection() : DbConnection( nullptr ) {}
// 	MAKE_MOCK(query, auto (const QString&) -> QStringList, override);
// 	MAKE_MOCK(singleQuery, auto (const QString&) -> QString, override);
// 	MAKE_MOCK(insert, auto (const QString&) -> int, override);
// 	MAKE_CONST_MOCK0(isConnected, auto () -> bool, override);
// };
// 
// inline auto qstring_re(QRegularExpression match) {
// 	return trompeloeil::make_matcher<QString>(
// 		[](QString value, QRegularExpression match_re) {
// 			return match_re.match(value).hasMatch();
// 		},
// 		[](std::ostream& os, QRegularExpression print_re) {
// 			os << " matching " << print_re.pattern().toStdString();
// 		},
// 		match
// 	);
// }
// namespace trompeloeil {
// 	template <>
// 	struct printer<QString> {
// 		static void print(std::ostream& os, const QString& val) {
// 			os << val.toStdString();
// 		}
// 	};
// }
// 
// void LoadPackageFilesTests::testLoadPackageHardMaskJobTrompeloeil() {
// 	//Arrange
// 	auto *job = new LoadPackageHardMaskJob();
// 	MockDbConnection mockConn;
// 	ALLOW_CALL(mockConn, singleQuery(trompeloeil::_)).RETURN(QString());
// 	int packageNum = 0;
// 	QString retVal;
// 	REQUIRE_CALL(mockConn, singleQuery(qstring_re(QRegularExpression("SELECT id FROM package WHERE name = '.*?' AND category = '.*?' LIMIT 1;"))))
// 		.TIMES(AT_LEAST(1))
// 		.LR_SIDE_EFFECT(retVal = QString().setNum(++packageNum))
// 		.LR_RETURN(std::ref(retVal));
// 	auto insertRE = QRegularExpression("INSERT INTO packageHardMask_temp \\(idPackage, dependAtom, comment\\) "
// 								"VALUES \\('(.*?)', '(.*?)', '(.*?)'\\)", QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption);
// 	QList<QStringList> masks;
// 	ALLOW_CALL(mockConn, insert(qstring_re(insertRE)))
// 		.LR_SIDE_EFFECT(masks.append(insertRE.match(_1).capturedTexts().sliced(1)))
// 		.RETURN(1);
// 	REQUIRE_CALL(mockConn, insert(trompeloeil::eq(QString("INSERT INTO packageHardMask SELECT * FROM packageHardMask_temp;"))))
// 		.RETURN(1);
// 	//Act
// 	job->impl( &mockConn );
// 	//Assert
// 	QCOMPARE_GT(packageNum, 1);
// 	bool found = false;
// 	QString mgw64run = QStringLiteral("dev-util/mingw64-runtime");
// 	while (!masks.isEmpty()) {
// 		auto mask = masks.takeLast();
// 		if ( mgw64run.compare(mask[1]) == 0 ) {
// 			found = true;
// 			QVERIFY(mask[2].startsWith(" Diego E. Pettenò <flameeyes@gentoo.org> (2009-01-03)"));
// 			break;
// 		}
// 	}
// 	QVERIFY2(found, "Should have found a mask for dev-util/mingw64-runtime");
// 	delete job;
// }

QRegularExpression selectIdQuery("SELECT id FROM package WHERE name = '.*?' AND category = '.*?' LIMIT 1;");
void LoadPackageFilesTests::testLoadPackageHardMaskJobFakeIt() {
	//Arrange
	KurooConfig::setRepoLocations( ConfigDialog::readReposConf( nullptr ) );
	auto* job = new LoadPackageHardMaskJob();
	Mock<DbConnection> mockConnection;
	When(Method(mockConnection, singleQuery)).AlwaysReturn(QString());
	int packageNum = 0;
	When(Method(mockConnection, singleQuery).Matching([](auto query) { return selectIdQuery.match(query).hasMatch(); }))
		.AlwaysDo([&packageNum](auto _) { return QString().setNum(++packageNum); });
	auto insertRE = QRegularExpression("INSERT INTO packageHardMask_temp \\(idPackage, dependAtom, comment\\) "
								"VALUES \\('(.*?)', '(.*?)', '(.*?)'\\)", QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption);
	QList<QStringList> masks;
	When(Method(mockConnection, insert).Matching([&insertRE](auto query) { return insertRE.match(query).hasMatch(); }))
		.AlwaysDo([&insertRE, &masks](auto query) { masks.append(insertRE.match(query).capturedTexts().sliced(1)); return 1;});
	When(Method(mockConnection, insert).Matching([](QString query) { return query == "INSERT INTO packageHardMask SELECT * FROM packageHardMask_temp;"; }))
		.Return(1);
	//Act
	job->impl(&mockConnection.get());
	//Assert
	QCOMPARE_GT(packageNum, 1);
	bool found = false;
	QString mgw64run = QStringLiteral("dev-util/mingw64-runtime");
	while (!masks.isEmpty()) {
		auto mask = masks.takeLast();
		if ( mgw64run.compare(mask[1]) == 0 ) {
			found = true;
			QVERIFY(mask[2].startsWith(" Diego E. Pettenò <flameeyes@gentoo.org> (2009-01-03)"));
			break;
		}
	}
	QVERIFY2(found, "Should have found a mask for dev-util/mingw64-runtime");
	delete job;
}

void LoadPackageFilesTests::testLoadPackageKeywordsJob() {
	//Arrange
	QString flatKeywordsFileName("package.keywords");
	KurooConfig::setDefaultFilePackageKeywords( TestHelpers::baseTestDir->path() + u'/' + flatKeywordsFileName );
	TestHelpers::writeFilesInTestDir({{flatKeywordsFileName, "app-portage/kuroo **\n#=games-strategy/freeorion-9999 **"}});
	auto* job = new LoadPackageKeywordsJob();
	Mock<DbConnection> mockConnection;
	When(Method(mockConnection, singleQuery)).AlwaysReturn(QString());
	int packageNum = 0;
	When(Method(mockConnection, singleQuery).Matching([](auto query) { return selectIdQuery.match(query).hasMatch(); }))
		.AlwaysDo([&packageNum](auto _) { return QString().setNum(++packageNum); });
	auto insertRE = QRegularExpression("INSERT INTO packageKeywords_temp \\(idPackage, keywords\\) VALUES \\('(.*?)', '(.*?)'\\);");
	QList<QStringList> keywords;
	When(Method(mockConnection, insert).Matching([&insertRE](auto query) { return insertRE.match(query).hasMatch(); }))
		.AlwaysDo([&insertRE, &keywords](auto query) { keywords.append(insertRE.match(query).capturedTexts().sliced(1)); return 1; });
	When(Method(mockConnection, insert).Using(Eq("INSERT INTO packageKeywords SELECT * FROM packageKeywords_temp;"))).Return(1);
	//Act
	job->impl(&mockConnection.get());
	//Assert
	QCOMPARE(packageNum, 1);
	QCOMPARE(keywords, QList<QStringList>({{"1", "**"}}));
	delete job;
}
