/*
 * 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 <QRegularExpression>
#include <QSettings>

#include "src/datovka_shared/identifiers/account_id.h"
#include "src/datovka_shared/log/log.h"
#include "src/datovka_shared/settings/prefs.h"
#include "src/datovka_shared/settings/prefs_helper.h"
#include "src/settings/accounts.h"
#include "src/settings/convert_to_prefs.h"
#include "src/settings/prefs_specific.h"

/*!
 * @brief Returns account identifiers in credentials order.
 */
static
QStringList accountIds(const QSettings &settings)
{
	QStringList accountIds;

	foreach (const QString credGroup, AccountsMap::sortedCredentialGroups(settings)) {
		accountIds.append(
		    PrefsHelper::accountIdentifier(
		        settings.value(credGroup + "/username").toString(),
		        settings.value(credGroup + "/test_account").toBool()));
	}

	return accountIds;
}

/*!
 * @brief Converts account tree settings.
 */
static
void convertAccountTree(QSettings &settings, Prefs &prefs)
{
	const QStringList accountsIdList = accountIds(settings);

	settings.beginGroup("account_tree");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting account tree expansion settings.");
	}
	for (int i = 0; i < keys.size(); ++i) {
		const QStringList keyIndexes = keys[i].split("_");
		if (keyIndexes.size() < 3) {
			continue;
		}
		const QString accountId = accountsIdList[keyIndexes[2].toInt()];

		/* Top level item. */
		switch (keyIndexes.size()) {
		case 3: /* Top level item. */
			prefs.setBoolVal(
			    "window.main.account_tree.account." + accountId + ".collapsed",
			     settings.value(keys[i]).toBool());
			break;
		case 4: /* Expanded all item. */
			prefs.setBoolVal(
			    "window.main.account_tree.account." + accountId + ".collapsed." + keyIndexes[3],
			    settings.value(keys[i]).toBool());
			break;
		case 5: /* Expanded child items of all. */
			prefs.setBoolVal(
			    "window.main.account_tree.account." + accountId + ".collapsed." + keyIndexes[3] + "." + keyIndexes[4],
			    settings.value(keys[i]).toBool());
			break;
		default:
			break;
		}
	}

	/* Remove all keys in current group 'account_tree'. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Converts information about message list column widths.
 */
static
void convertColumnWidths(QSettings &settings, Prefs &prefs)
{
	settings.beginGroup("column_widths");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting message list column width settings.");
	}
	foreach (const QString &key, keys) {
		if ((key == "received_1") || (key == "received_2") || (key == "sent_2")) {
			bool iOk = false;
			qint64 val = settings.value(key).toLongLong(&iOk);
			if (Q_UNLIKELY(!iOk)) {
				continue;
			}
			if (key == "received_1") {
				prefs.setIntVal(
				    "window.main.message_list.column.annotation.width",
				    val);
			} else if (key == "received_2") {
				prefs.setIntVal(
				    "window.main.message_list.column.sender.width",
				    val);
			} else if (key == "sent_2") {
				prefs.setIntVal(
				    "window.main.message_list.column.recipient.width",
				    val);
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Convert connection (proxy) settings.
 */
static
void convertConnection(QSettings &settings, Prefs &prefs)
{
#define NO_PROXY_STR "None"
	settings.beginGroup("connection");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting connection settings.");
	}
	foreach (const QString &key, keys) {
		if (key == "http_proxy") {
			QString val = settings.value(key).toString();
			if (val == NO_PROXY_STR) {
				val.clear(); /* Use blank instead. */
			}
			if (!val.isEmpty()) {
				prefs.setStrVal("network.proxy.http", val);
			}
		} else if (key == "http_proxy_password") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal("network.proxy.http.password.base64", val);
			}
		} else if (key == "http_proxy_username") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal("network.proxy.http.username", val);
			}
		} else if (key == "https_proxy") {
			QString val = settings.value(key).toString();
			if (val == NO_PROXY_STR) {
				val.clear(); /* Use blank instead. */
			}
			if (!val.isEmpty()) {
				prefs.setStrVal("network.proxy.https", val);
			}
		} else if (key == "https_proxy_password") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal("network.proxy.https.password.base64", val);
			}
		} else if (key == "https_proxy_username") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal("network.proxy.https.username", val);
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
#undef NO_PROXY_STR
}

/*!
 * @brief Convert some data from the credentials into preferences database.
 */
static
void convertCredentialsPartial(QSettings &settings, Prefs &prefs)
{
	const QStringList credGroups = AccountsMap::sortedCredentialGroups(settings);
	if (credGroups.size() > 0) {
		logInfoNL("%s", "Converting some credential settings.");
	}

	foreach (const QString &credGroup, credGroups) {
		settings.beginGroup(credGroup);

		const AcntId acntId(settings.value("username").toString(),
		    settings.value("test_account").toBool());
		const QString accountId = PrefsHelper::accountIdentifier(
		    acntId.username(), acntId.testing());

		{
			bool iOk = false;
			qint64 val = settings.value("last_message_id").toLongLong(&iOk);
			if (iOk && (val > 0)) {
				prefs.setIntVal(
				    "window.main.account." + accountId + ".last.selected.message_id",
				    val);
			}
		}
		{
			QString val = settings.value("last_save_attach_path").toString();
			if (!val.isEmpty()) {
				PrefsSpecific::setAcntSaveAttachDir(prefs,
				    acntId, val);
			}
		}
		{
			QString val = settings.value("last_add_attach_path").toString();
			if (!val.isEmpty()) {
				PrefsSpecific::setAnctLoadAttachDir(prefs,
				    acntId, val);
			}
		}
		{
			QString val = settings.value("last_export_corresp_path").toString();
			if (!val.isEmpty()) {
				PrefsSpecific::setAcntCorrespondenceDir(prefs,
				    acntId, val);
			}
		}
		{
			QString val = settings.value("last_export_zfo_path").toString();
			if (!val.isEmpty()) {
				PrefsSpecific::setAcntZfoDir(prefs,
				    acntId, val);
			}
		}

		/* Remove some keys in current group. */
		settings.remove("last_message_id");
		settings.remove("last_save_attach_path");
		settings.remove("last_add_attach_path");
		settings.remove("last_export_corresp_path");
		settings.remove("last_export_zfo_path");
		settings.endGroup();
	}

	settings.sync();
}

/*!
 * @brief Converts information about last selected account.
 */
static
void convertDefaultAccount(QSettings &settings, Prefs &prefs)
{
	const QStringList accountsIdList = accountIds(settings);

	settings.beginGroup("default_account");
	QString soughtUsername = settings.value("username").toString();
	if (!soughtUsername.isEmpty()) {
		logInfoNL("%s", "Converting default account settings.");

		foreach (const QString &accountId, accountsIdList) {
			QString username;
			bool testing = false;
			if (Q_UNLIKELY(!PrefsHelper::accountIdentifierData(
			                   accountId, username, testing))) {
				continue;
			}
			if (username == soughtUsername) {
				prefs.setStrVal(
				    "window.main.account_tree.selected_account",
				    accountId);
				break;
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Converts information about last directories.
 */
static
void convertLastDirectories(QSettings &settings, Prefs &prefs)
{
	settings.beginGroup("last_directories");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting last directories settings.");
	}
	foreach (const QString &key, keys) {
		if (key == "on_import_database_dir_activate") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal("global.path.import.database",
				    val);
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Converts information about sort order of message list.
 */
static
void convertMessageOrdering(QSettings &settings, Prefs &prefs)
{
	settings.beginGroup("message_ordering");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting message list ordering settings.");
	}
	foreach (const QString &key, keys) {
		if (key == "sort_column") {
			bool iOk = false;
			int val = settings.value(key).toLongLong(&iOk);
			if (Q_UNLIKELY(!iOk)) {
				continue;
			}
			prefs.setIntVal(
			    "window.main.message_list.sort.column_number", val);
		} else if (key == "sort_order") {
			const QString strVal = settings.value(key).toString();
			if (strVal == "SORT_ASCENDING") {
				prefs.setIntVal(
				    "window.main.message_list.sort.order", 0);
			} else if (strVal == "SORT_DESCENDING") {
				prefs.setIntVal(
				    "window.main.message_list.sort.order", 1);
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Converts main window splitter settings.
 */
static
void convertPanes(QSettings &settings, Prefs &prefs)
{
	settings.beginGroup("panes");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting splitter position settings.");
	}
	foreach (const QString &key, keys) {
		if ((key == "hpaned1") || (key == "message_pane") || (key == "message_display_pane")) {
			bool iOk = false;
			qint64 val = settings.value(key).toLongLong(&iOk);
			if (Q_UNLIKELY(!iOk)) {
				continue;
			}
			if (key == "hpaned1") {
				prefs.setIntVal("window.main.pane.account_tree", val);
			} else if (key == "message_pane") {
				prefs.setIntVal("window.main.pane.message_list", val);
			} else if (key == "message_display_pane") {
				prefs.setIntVal("window.main.pane.message_info", val);
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Converts preferences.
 */
static
void convertPreferences(QSettings &settings, Prefs &prefs)
{
	settings.beginGroup("preferences");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting preferences settings.");
	}
	foreach (const QString &key, keys) {
		if (key == "auto_download_whole_messages") {
			prefs.setBoolVal(
			    "action.sync_account.tie.action.download_message",
			    settings.value(key).toBool());
		} else if (key == "store_messages_on_disk") {
			prefs.setBoolVal(
			    "storage.database.messages.on_disk.enabled",
			    settings.value(key).toBool());
		} else if (key == "store_additional_data_on_disk") {
			prefs.setBoolVal(
			    "storage.database.accounts.on_disk.enabled",
			    settings.value(key).toBool());
		} else if (key == "download_on_background") {
			prefs.setBoolVal(
			    "window.main.action.sync_all_accounts.background.enabled",
			    settings.value(key).toBool());
		} else if (key == "timer_value") {
			prefs.setIntVal(
			    "window.main.action.sync_all_accounts.background.timer.period.minutes",
			    settings.value(key).toLongLong());
		} else if (key == "download_at_start") {
			prefs.setBoolVal(
			    "window.main.on.start.tie.action.sync_all_accounts",
			    settings.value(key).toBool());
		} else if (key == "check_new_versions") {
			prefs.setBoolVal("application.version.check.enabled",
			    settings.value(key).toBool());
		} else if (key == "isds_download_timeout_ms") {
			prefs.setIntVal("network.isds.communication.timeout.ms",
			    settings.value(key).toLongLong());
		} else if (key == "message_mark_as_read_timeout") {
			prefs.setIntVal(
			    "window.main.message_list.read.mark_read.timeout.ms",
			    settings.value(key).toLongLong());
		} else if (key == "certificate_validation_date") {
			prefs.setIntVal("crypto.message.validation.date.type",
			    settings.value(key).toLongLong());
		} else if (key == "check_crl") {
			prefs.setBoolVal("crypto.crl.check.enabled",
			    settings.value(key).toBool());
		} else if (key == "timestamp_expir_before_days") {
			prefs.setIntVal(
			    "crypto.message.timestamp.expiration.notify_ahead.days",
			    settings.value(key).toLongLong());
		} else if (key == "after_start_select") {
			prefs.setIntVal("window.main.message_list.select.on.account_change",
			    settings.value(key).toLongLong());
		} else if (key == "toolbar_button_style") {
			prefs.setIntVal("window.main.toolbar.button.style",
			    settings.value(key).toLongLong());
		} else if (key == "use_global_paths") {
			PrefsSpecific::setUseGlobalPaths(prefs,
			    settings.value(key).toBool());
		} else if (key == "save_attachments_path") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				PrefsSpecific::setSaveAttachDir(prefs, val);
			}
		} else if (key == "add_file_to_attachments_path") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				PrefsSpecific::setLoadAttachDir(prefs, val);
			}
		} else if (key == "all_attachments_save_zfo_msg") {
			prefs.setBoolVal(
			    "action.save_all_attachments.tie.action.export_message_zfo",
			    settings.value(key).toBool());
		} else if (key == "all_attachments_save_zfo_delinfo") {
			prefs.setBoolVal(
			    "action.save_all_attachments.tie.action.export_acceptance_info_zfo",
			    settings.value(key).toBool());
		} else if (key == "all_attachments_save_pdf_msgenvel") {
			prefs.setBoolVal(
			    "action.save_all_attachments.tie.action.export_envelope_pdf",
			    settings.value(key).toBool());
		} else if (key == "all_attachments_save_pdf_delinfo") {
			prefs.setBoolVal(
			    "action.save_all_attachments.tie.action.export_acceptance_info_pdf",
			    settings.value(key).toBool());
		} else if (key == "delivery_info_for_every_file") {
			prefs.setBoolVal(
			    "action.save_all_attachments.every_attachment.export_acceptance_info",
			    settings.value(key).toBool());
		} else if (key == "message_filename_format") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal(
				    "file_system.format.filename.message", val);
			}
		} else if (key == "delivery_filename_format") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal(
				    "file_system.format.filename.acceptance_info",
				    val);
			}
		} else if (key == "attachment_filename_format") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal(
				    "file_system.format.filename.attachment",
				    val);
			}
		} else if (key == "delivery_filename_format_all_attach") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal(
				    "file_system.format.filename.every_attachment_acceptance_info",
				    val);
			}
		} else if (key == "language") {
			QString val = settings.value(key).toString();
			if (!val.isEmpty()) {
				prefs.setStrVal("translation.language", val);
			}
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

/*!
 * @brief Converts main window position settings.
 */
static
void convertWindowPosition(QSettings &settings, Prefs &prefs)
{
	settings.beginGroup("window_position");
	const QStringList keys = settings.childKeys();
	if (keys.size() > 0) {
		logInfoNL("%s", "Converting window position settings.");
	}
	foreach (const QString &key, keys) {
		if ((key == "x") || (key == "y") || (key == "w") || (key == "h")) {
			bool iOk = false;
			qint64 val = settings.value(key).toLongLong(&iOk);
			if (Q_UNLIKELY(!iOk)) {
				continue;
			}
			if ((key == "x") || (key == "y")) {
				prefs.setIntVal("window.main.position." + key, val);
			} else if (key == "w") {
				prefs.setIntVal("window.main.position.width", val);
			} else if (key == "h") {
				prefs.setIntVal("window.main.position.height", val);
			}
		} else if (key == "max") {
			prefs.setBoolVal("window.main.position.maximised" + key,
			    settings.value(key).toBool());
		}
	}

	/* Remove all keys in current group. */
	settings.remove("");
	settings.endGroup();

	settings.sync();
}

void iniPreferencesToPrefs(QSettings &settings, Prefs &prefs)
{
	convertAccountTree(settings, prefs);
	convertColumnWidths(settings, prefs);
	convertConnection(settings, prefs);
	convertCredentialsPartial(settings, prefs);
	convertDefaultAccount(settings, prefs);
	convertLastDirectories(settings, prefs);
	convertMessageOrdering(settings, prefs);
	convertPanes(settings, prefs);
	convertPreferences(settings, prefs);
	convertWindowPosition(settings, prefs);
}
