/*
 * Copyright (C) 2014-2024 CZ.NIC
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 * In addition, as a special exception, the copyright holders give
 * permission to link the code of portions of this program with the
 * OpenSSL library under certain conditions as described in each
 * individual source file, and distribute linked combinations including
 * the two.
 */

#include <cinttypes> /* PRId64 */
#include <cstdlib>
#include <QCoreApplication>
#include <QFile>
#include <QUrl>

#include "src/datovka_shared/log/log.h"
#include "src/io/tag_client.h"
#include "src/json/tag_assignment_hash.h"
#include "src/json/tag_entry.h"
#include "src/json/tag_message_id.h"
#include "src/json/tag_text_search_request.h"
#include "src/json/tag_text_search_request_hash.h"
#include "src/json/srvr_profile.h"

/* https://stackoverflow.com/a/4182144 */
class Task : public QObject {
	Q_OBJECT
public:
	Task(QObject *parent = Q_NULLPTR)
	    : QObject(parent)
	{ }

public slots:
	void run(void)
	{
		// Do processing here.

		emit finished();
	}

signals:
	void finished(void);
};

#include "main_client_tag.moc"

static
int allocGlobLog(void)
{
	GlobInstcs::logPtr = new (::std::nothrow) LogDevice;
	if (Q_UNLIKELY(Q_NULLPTR == GlobInstcs::logPtr)) {
		return -1;
	}

	return 0;
}

static
void deallocGlobLog(void)
{
	if (Q_NULLPTR != GlobInstcs::logPtr) {
		delete GlobInstcs::logPtr;
		GlobInstcs::logPtr = Q_NULLPTR;
	}
}

static
QSslCertificate readCertPem(const QString &fName)
{
	QFile caCertFile(fName);
	if (caCertFile.exists()) {
		if (caCertFile.open(QIODevice::ReadOnly)) {
			QSslCertificate cert(caCertFile.readAll(), QSsl::Pem);
			caCertFile.close();
			if (!cert.isNull()) {
				return cert;
			} else {
				logErrorNL(
				    "Could not read CA certificate from file '%s'.",
				    fName.toUtf8().constData());
			}
		}
	} else {
		logWarningNL("CA certificate file '%s' doesn't exist.",
		    fName.toUtf8().constData());
	}

	return QSslCertificate();
}

#define CA_CERT_FILE "tag_server_ca.pem"

int main(int argc, char *argv[])
{
	QCoreApplication app(argc, argv);

	if (0 != allocGlobLog()) {
		/* Cannot continue without logging facility. */
		return EXIT_FAILURE;
	}

	/* Log all. */
	GlobInstcs::logPtr->setLogLevelBits(LogDevice::LF_STDERR, LOGSRC_ANY,
	    LOG_UPTO(LOG_DEBUG));
	GlobInstcs::logPtr->setDebugVerbosity(3);
	//GlobInstcs::logPtr->setLogVerbosity(3);

	TagClient tagClient;

	{
		{
			QUrl url("https://localhost");
			url.setPort(8888);
			tagClient.setBaseUrl(url);
		}
		{
			QUrl url("wss://localhost");
			url.setPort(8888);
			tagClient.setWebSockUrl(url);
		}

		QSslCertificate caCert = readCertPem(CA_CERT_FILE);
		if (!caCert.isNull()) {
			tagClient.setCaCertificate(caCert);
		}

	}

	bool success = tagClient.netConnectUsernamePwd("user", "01234567");
	if (success) {
		logInfoNL("%s", "Connected to server.");
	} else {
		logErrorNL("%s", "Connection failed.");
		return EXIT_FAILURE;
	}

	success = tagClient.isConnected();
	if (success) {
		logInfoNL("%s", "Still connected to server.");
	} else {
		logErrorNL("%s", "Connection failed.");
		return EXIT_FAILURE;
	}

	qint64 profileId = -1;
	{
		Json::SrvrProfileList profiles;
		success = tagClient.listProfiles(profiles);
		if (success) {
			if (profiles.size() > 0) {
				logInfoNL("Picking profile '%" PRId64 "': '%s'[%s]",
				    UGLY_QINT64_CAST profiles.at(0).id(),
				    profiles.at(0).name().toUtf8().constData(),
				    profiles.at(0).description().toUtf8().constData());
				profileId = profiles.at(0).id();
			} else {
				logErrorNL("%s", "Didn't receive any profiles.");
				return EXIT_FAILURE;
			}
		} else {
			logErrorNL("%s", "Connection failed.");
			return EXIT_FAILURE;
		}
	}

	success = tagClient.selectProfile(profileId);
	if (success) {
		logInfoNL("Selected profile '%" PRId64 "'.",
		    UGLY_QINT64_CAST profileId);
	} else {
		logErrorNL("Couldn't select profile '%" PRId64 "'.",
		    UGLY_QINT64_CAST profileId);
		return EXIT_FAILURE;
	}

	{
		Json::TagEntryList list({
		    Json::TagEntry(0, "Tag name", "aaaaaa", QString())});
		success = tagClient.insertTags(list);
		if (success) {
		} else {
			logErrorNL("Cannot insert tags '%s'.", list.toJsonData().constData());
			//return EXIT_FAILURE;
		}
	}

	{
		Json::TagEntryList list;
		success = tagClient.getAllTags(list);
		if (success) {
			logInfoNL("Got tag list '%s'.", list.toJsonData().constData());
		} else {
			logErrorNL("%s", "Cannot list all tags.");
			return EXIT_FAILURE;
		}
	}

	{
		Json::TagAssignmentCommand command;
		command[Json::TagMsgId(Isds::Type::BOOL_TRUE, 1001)] = Json::Int64StringList({1});
		command[Json::TagMsgId(Isds::Type::BOOL_TRUE, 1002)] = Json::Int64StringList({1});
		success = tagClient.assignTagsToMsgs(command);
		if (success) {
		} else {
			logErrorNL("Cannot assign tag to message '%s'.", command.toJsonData().constData());
			//return EXIT_FAILURE;
		}
	}

	{
		Json::TagTextSearchRequestList requests({Json::TagTextSearchRequest("tag", Json::TagTextSearchRequest::ST_SUBSTRING)});
		Json::TagTextSearchRequestToIdenfifierHash responses;
		success = tagClient.getMsgIdsContainSearchTagText(requests, responses);
		if (success) {
			logInfoNL("Got message lists '%s'.", responses.toJsonData().constData());
		} else {
			logErrorNL("Cannot search for messages '%s'.", requests.toJsonData().constData());
		}
	}

	{
		const Json::Int64StringList ids({1});
		Json::TagIdToAssignmentCountHash assignmentCounts;
		success = tagClient.getTagAssignmentCounts(ids, assignmentCounts);
		if (success) {
			logInfoNL("Got assignment counts '%s'.", assignmentCounts.toJsonData().constData());
		} else {
			logErrorNL("Cannot get assignment counts '%s'.", ids.toJsonData().constData());
		}
	}

	success = tagClient.disconnect();
	if (success) {
		logInfoNL("%s", "Disconnected from server.");
	} else {
		logErrorNL("%s", "Connection failed.");
		return EXIT_FAILURE;
	}

	deallocGlobLog();

	return EXIT_SUCCESS;
}
