/*
 * Copyright (C) 2014-2022 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 <QFile>
#include <QSslCertificate>
#include <QUrl>

#include "src/datovka_shared/log/log.h"
#include "src/io/tag_client.h"
#include "src/io/tag_container.h"
#include "src/json/srvr_profile.h"
#include "src/settings/tag_container_settings.h"
#include "src/settings/tag_container_settings_apply.h"

QSslCertificate readSslCert(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 in PEM format from file '%s'.",
				    fName.toUtf8().constData());
			}
		}
		if (caCertFile.open(QIODevice::ReadOnly)) {
			QSslCertificate cert(caCertFile.readAll(), QSsl::Der);
			caCertFile.close();
			if (!cert.isNull()) {
				return cert;
			} else {
				logErrorNL(
				    "Could not read CA certificate in DER format from file '%s'.",
				    fName.toUtf8().constData());
			}
		}
	} else {
		logWarningNL("CA certificate file '%s' doesn't exist.",
		    fName.toUtf8().constData());
	}

	return QSslCertificate();
}

void applyCompleteTagContainerSettings(const TagContainerSettings &set,
    TagContainer &tagCont)
{
	/* Database backend is set up during application start. */
	TagClient *tagClient = tagCont.backendClient();

	if (tagClient != Q_NULLPTR) {
		tagClient->netDisconnect();
	}

	tagCont.selectBackend(set.backend());

	if (tagClient != Q_NULLPTR) {
		/* Apply connection settings. */
		{
			QUrl url("https://" + set.clientHostname());
			url.setPort(set.clientPort());
			tagClient->setBaseUrl(url);
		}
		{
			QUrl url("wss://" + set.clientHostname());
			url.setPort(set.clientPort());
			tagClient->setWebSockUrl(url);
		}
		{
			if (!set.certPath().isEmpty()) {
				QSslCertificate caCert = readSslCert(set.certPath());
				if (!caCert.isNull()) {
					tagClient->setCaCertificate(caCert);
				}
			}
		}

		/* Connect only if client backend selected. */
		if (tagCont.backend() == TagContainer::BACK_CLIENT) {
			bool success = tagClient->netConnectUsernamePwd(
			    set.username(), set.password());
			if (success) {
				logInfoNL("%s", "Connected to tag server.");

				applyProfileSettings(set, tagCont);
			} else {
				logErrorNL("%s", "Connection to tag server failed.");
			}
		}
	}
}

void applyProfileSettings(const TagContainerSettings &set,
    TagContainer &tagCont)
{
	TagClient *tagClient = tagCont.backendClient();

	if (tagClient != Q_NULLPTR) {
		if (Q_UNLIKELY(!tagClient->isConnected())) {
			logErrorNL("%s", "Not connected to tag server.");
			return;
		}

		Json::SrvrProfile foundProfile;

		{
			Json::SrvrProfileList profiles;
			bool success = tagClient->listProfiles(profiles);
			if (success) {
				for (const Json::SrvrProfile &profile : profiles) {
					if (profile.id() == set.profileId()) {
						foundProfile = profile;
						break;
					}
				}
				if (Q_UNLIKELY(foundProfile.isNull())) {
					logWarningNL(
					    "Profile '%" PRId64 "' '%s' not found.",
					    UGLY_QINT64_CAST set.profileId(),
					    set.profileName().toUtf8().constData());
				}
			} else {
				logErrorNL("%s", "Cannot download profile list.");
			}
		}

		if (!foundProfile.isNull()) {
			bool success = tagClient->selectProfile(foundProfile.id());
			if (Q_UNLIKELY(!success)) {
				logErrorNL(
				    "Cannot select profile '%" PRId64 "' '%s'.",
				    UGLY_QINT64_CAST foundProfile.id(),
				    foundProfile.name().toUtf8().constData());
			}
		}
	}
}
