/*
 * Copyright (C) 2014-2026 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 <QCoreApplication>
#include <QDir>
#include <QDirIterator>
#include <QFile>
#include <QFileDialog>
#include <QFileInfo>
#include <QMessageBox>
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
#  include <QStorageInfo>
#else /* < Qt-5.4 */
#  warning "Compiling against version < Qt-5.4 which does not have QStorageInfo."
#endif /* >= Qt-5.4 */
#include <QStringBuilder>

#include "src/datovka_shared/identifiers/account_id.h"
#include "src/datovka_shared/io/filesystem.h"
#include "src/datovka_shared/log/log.h"
#include "src/dimensions/dimensions.h"
#include "src/global.h"
#include "src/gui/datovka.h"
#include "src/gui/dlg_convert_import_from_mobile_app.h"
#include "src/io/account_db.h"
#include "src/io/db_convert_mobile_to_desktop.h"
#include "src/io/imports.h"
#include "src/io/isds_helper.h"
#include "src/io/isds_sessions.h"
#include "src/io/load_mobile_zip_package.h"
#include "src/io/message_db_set.h"
#include "src/model_interaction/account_interaction.h"
#include "src/settings/accounts.h"
#include "src/settings/preferences.h"
#include "src/settings/prefs_specific.h"
#include "src/views/table_home_end_filter.h"
#include "src/views/table_space_selection_filter.h"
#include "src/views/table_tab_ignore_filter.h"
#include "ui_dlg_convert_import_from_mobile_app.h"

#define UNZIP_WORKING_DIR_NAME "unzip_data_tmp"

static const QString dlgName("convert_import_from_mobile_app");
static const QString accountTableName("account_list");

/*!
 * @brief Move message database (including acompanying) to final destination.
 *
 * @param[in] srcDbFilePath Source message database path.
 * @param[in] tgtDirPath Target directory.
 * @return Full path where message database were moved.
 */
static
QString moveMsgDb(const QString &srcDbFilePath, const QString &tgtDirPath)
{
	const QFileInfo fi(srcDbFilePath);
	if (Q_UNLIKELY(!fi.isFile())) {
		return QString();
	}

	const QString tgtDbFilePath = tgtDirPath % QStringLiteral("/") % fi.fileName();
	const QString srcDbDirPath = fi.dir().absolutePath() % QStringLiteral("/") % fi.baseName();
	const QString tgtDbDirPath = tgtDirPath % QStringLiteral("/") % fi.baseName();

	/* Move database file. Source file must exist. */
	if (Q_UNLIKELY(!QFile(srcDbFilePath).rename(tgtDbFilePath))) {
		logErrorNL("Failed to move file '%s' to '%s'.",
		    srcDbFilePath.toUtf8().constData(),
		    tgtDbFilePath.toUtf8().constData());
		return QString();
	}

	/* Move accompanying directory. */
	{
		const QFileInfo dirFi(srcDbDirPath);
		/* Directory may not exist. */
		if (dirFi.exists()) {
			/* Cannot use QDir::rename() here. */
			/* Copy. */
			if (Q_UNLIKELY(!copyDirRecursively(srcDbDirPath, tgtDirPath, true))) {
				logErrorNL("Failed to copy directory '%s' to '%s'.",
				    srcDbDirPath.toUtf8().constData(),
				    tgtDbDirPath.toUtf8().constData());
				return QString();
			}
			/* And delete source. */
			QDir(srcDbDirPath).removeRecursively();
		}
	}

	return tgtDbFilePath;
}

/*!
 * @brief Create subdirectory in the path.
 *
 * @param[in] sourcePath Source path.
 * @param[in] subDir Subdirectory name.
 * @return Absolute subdirectory path.
 */
static
QString createSubDirPath(const QString &sourcePath, const QString &subDir)
{
	QDir dir(sourcePath % QDir::separator() % subDir);
	dir.removeRecursively();
	if (Q_UNLIKELY((!dir.exists()) && (!dir.mkpath(".")))) {
		return QString();
	}
	return dir.absolutePath();
}

/*!
 * @brief Add accounts into the account model.
 *
 * @param[in,out] model Account model.
 * @param[in] acntIdList account id list.
 */
static
void addAccountsIntoModel(BackupSelectionModel &model,
    const QList<AcntId> &acntIdList)
{
	for (const AcntId &acntId : acntIdList) {
		model.appendData(false, QString(), acntId, QString());
	}
}

/*!
 * @brief Remove directory recursively.
 *
 * @param[in] dirPath Directory path.
 */
static inline
void removeRecursivelyDir(const QString &dirPath)
{
	QDir dir(dirPath);
	dir.removeRecursively();
}

/*!
 * @brief Check if message database exists in the target path.
 *
 * @param[in] filename Message database file name.
 * @param[in] targetPath Target location.
 * @return True if success.
 */
static
bool msgDbExists(const QString &filename, const QString &targetPath)
{
	return QFile::exists(targetPath % QDir::separator() % filename);
}

/*!
 * @brief Check if some account database exists in the application data location.
 *
 * @param[in] dbPathList List of message database paths.
 * @param[in] targetPath Path to application data location.
 * @return True if some account database with same user name exists.
 */
static
bool isSomeDbFileAvailable(const QStringList &dbPathList,
    const QString &targetPath)
{
	for (const QString &dbFile : dbPathList) {
		if (Q_UNLIKELY(msgDbExists(QFileInfo(dbFile).fileName(),
		        targetPath))) {
			return true;
		}
	}
	return false;
}

/*!
 * @brief Compute total database size. Only file sizes are added together.
 *
 * @param[in] dbFilePath Path to database file.
 * @param[in] accompanyingDir If true then total size of accompanying directory is also added to the sum.
 * @return Total database size.
 */
static
bool computeDabaseSize(const QString &dbFilePath, bool accompanyingDir)
{
	qint64 totalBytes = 0;
	QString dbDirPath;
	{
		const QFileInfo fi(dbFilePath);
		totalBytes = fi.size();
		if (accompanyingDir) {
			dbDirPath = fi.dir().absolutePath() % QStringLiteral("/") % fi.baseName();
			const QFileInfo dirFi(dbDirPath);
			if (Q_UNLIKELY(!dirFi.exists())) {
				dbDirPath.clear();
			}
		}
	}

	if (Q_UNLIKELY(dbDirPath.isEmpty())) {
		return totalBytes;
	}

	QDirIterator it(dbDirPath, QStringList(), QDir::Files,
	    QDirIterator::Subdirectories);
	while (it.hasNext()) {
		it.next(); /* Must be before it.fileInfo(). */
		totalBytes += it.fileInfo().size();
	}

	return totalBytes;
}

/*!
 * @brief Check available space.
 *
 * @param[in] dbPathList List of message database paths.
 * @param[in] targetPath Path to application data location.
 * @return True if success.
 */
static
bool checkAvailableSpace(const QStringList &dbPathList,
    const QString &targetPath)
#if (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
{
	qint64 dbTotalSizeBytes = 0;
	for (const QString &dbFile : dbPathList) {
		dbTotalSizeBytes += computeDabaseSize(dbFile, true);
	}
	return (QStorageInfo(targetPath).bytesAvailable() > dbTotalSizeBytes);
}
#else /* < Qt-5.4 */
{
	return true;
}
#endif /* >= Qt-5.4 */

/*!
 * @brief Move message databases and zfo files to application data location.
 *
 * @param[in] sourcePath Source data location.
 * @param[in] dbList List of message databases.
 * @param[in] targetPath Path to application data location.
 * @return True if success.
 */
static
bool moveDbToAppDataLocation(const QString &sourcePath,
    const QStringList &dbList, const QString &targetPath)
{
	for (const QString &dbFile : dbList) {
		/* Move message databases. */
		const QString newFilePath = moveMsgDb(dbFile, targetPath);
		if (Q_UNLIKELY(newFilePath.isEmpty())) {
			return false;
		}
	}
	return true;
}

/*!
 * @brief Create account and add into model.
 *
 * @param[in] regularAccounts Account container.
 * @param[in] acntId Account id.
 * @param[in] targetPath Path to application data location.
 * @return True if success.
 */
static
bool createAccount(AccountsMap &regularAccounts, const AcntId &acntId,
    const QString &targetPath)
{
	/* Create account and add into global account container.*/
	AcntData itemSettings;
	itemSettings.setTestAccount(acntId.testing());
	itemSettings.setAccountName(acntId.username());
	itemSettings.setUserName(acntId.username());
	itemSettings.setLoginMethod(AcntSettings::LIM_UNAME_PWD);
	itemSettings.setPassword(QString());
	itemSettings.setRememberPwd(false);
	itemSettings.setSyncWithAll(false);
	itemSettings.setDbDir(targetPath, QString());

	return (0 == regularAccounts.addAccount(itemSettings));
}

/*!
 * @brief Get list of relevant account databases.
 *
 * @param[in] acntId Account id.
 * @param[in] dbPathList List of message database paths.
 * @return List of relevant account databases.
 */
static
QStringList getAcntMsgDbs(const AcntId &acntId, const QStringList &dbPathList)
{
	QStringList msgDbs;
	for (const QString &dbPath : dbPathList) {

		const QString dbFileName(QFileInfo(dbPath).fileName());
		QString dbUserName, dbYearFlag, errMsg;
		bool dbTestingFlag;

		/* Split and check the database file name. */
		if (MessageDbSet::isValidDbFileName(dbFileName, dbUserName,
		        dbYearFlag, dbTestingFlag, errMsg)) {
			if ((acntId.username() == dbUserName) &&
			        (acntId.testing() == dbTestingFlag)) {
				msgDbs.append(dbPath);
			}
		}
	}
	return msgDbs;
}

/*!
 * @brief Import messages from converted databases into exist account.
 *
 * @param[in] mw Pointer to main window.
 * @param[in] acntId Account id.
 * @param[in] dbPathList List of message database paths.
 * @param[in] parent Parent widget.
 * @return True if success.
 */
static
bool importDbMsgsIntoAcnt(MainWindow *mw, const AcntId &acntId,
    const QStringList &dbPathList, QWidget *parent)
{
	if (Q_UNLIKELY(Q_NULLPTR == mw)) {
		Q_ASSERT(0);
		return false;
	}

	enum AccountInteraction::AccessStatus status =
	    AccountInteraction::AS_ERR;
	QString dbDir, namesStr;

	MessageDbSet *dbSet = AccountInteraction::accessDbSet(acntId, status,
	    dbDir, namesStr);
	if (Q_UNLIKELY(Q_NULLPTR == dbSet)) {
		Q_ASSERT(0);
		return false;
	}

	QString dbId = GlobInstcs::accntDbPtr->dbId(
	    AccountDb::keyFromLogin(acntId.username()));
	if (Q_UNLIKELY(dbId.isEmpty())) {
		/* The data-box identifier is missing. Try downloading it. */
		if (!GlobInstcs::isdsSessionsPtr->isConnectedToIsds(acntId.username())) {
			if (mw != Q_NULLPTR) {
				if (Q_UNLIKELY(!mw->connectToIsds(acntId))) {
					QMessageBox::warning(parent,
					    DlgConvertImportFromMobileApp::tr("Couldn't Log into ISDS"),
					    DlgConvertImportFromMobileApp::tr("Couldn't log into ISDS from the data box '%1 (%2)'.\n\nPlease check the data box properties.\n\nImport for this data box will be aborted.")
					        .arg(GlobInstcs::acntMapPtr->acntData(acntId).accountName())
					        .arg(acntId.username()),
					    QMessageBox::Ok);
					return false;
				}
			}
		}

		if (Q_UNLIKELY(!IsdsHelper::getOwnerInfoFromLogin(acntId))) {
			QMessageBox::warning(parent,
			    DlgConvertImportFromMobileApp::tr("Couldn't Download Data Box Information"),
			    DlgConvertImportFromMobileApp::tr("Couldn't download information about the data box '%1 (%2)'.\n\nImport for this data box will be aborted.")
			        .arg(GlobInstcs::acntMapPtr->acntData(acntId).accountName())
			        .arg(acntId.username()),
			    QMessageBox::Ok);
			return false;
		}
		IsdsHelper::getUserInfoFromLogin(acntId);

		dbId = GlobInstcs::accntDbPtr->dbId(
		    AccountDb::keyFromLogin(acntId.username()));
	}
	if (Q_UNLIKELY(dbId.isEmpty())) {
		/*
		 * An attempt to download the data-box identifier should have
		 * already been performed.
		 */
		return false;
	}

	/* The import needs the dbId. */
	Imports::importDbMsgsIntoDatabase(*dbSet, dbPathList,
	    acntId.username(), dbId);
	return true;
}

DlgConvertImportFromMobileApp::DlgConvertImportFromMobileApp(MainWindow *mw,
    QWidget *parent)
    : QDialog(parent),
    m_ui(new (::std::nothrow) Ui::DlgConvertImportFromMobileApp),
    m_mw(mw),
    m_zipPath(),
    m_accountListProxyModel(this),
    m_dfltFilerLineStyleSheet(SortFilterProxyModel::invalidFilterEditStyle),
    m_accountSelectionModel(this)
{
	m_ui->setupUi(this);

	connect(m_ui->openZipButton, SIGNAL(clicked()),
	    this, SLOT(openZipPackage()));
	connect(m_ui->importButton, SIGNAL(clicked()),
	    this, SLOT(unzipAndImportMsgs()));

	connect(m_ui->accountFilterLine, SIGNAL(textChanged(QString)),
	    this, SLOT(filterAccount(QString)));
	connect(m_ui->allCheckBox, SIGNAL(stateChanged(int)),
	    this, SLOT(applyAllSelection(int)));

	m_ui->zipPackageInfoText->clear();
	m_ui->acntSelectionGroupBox->setEnabled(false);
	m_ui->importGroupBox->setEnabled(false);

	/* Set table view model and properties. */
	m_ui->accountTableView->setNarrowedLineHeight();
	m_ui->accountTableView->horizontalHeader()->setDefaultAlignment(
	    Qt::AlignVCenter | Qt::AlignLeft);
	m_ui->accountTableView->setSelectionMode(
	    QAbstractItemView::ExtendedSelection);
	m_ui->accountTableView->setSelectionBehavior(
	    QAbstractItemView::SelectRows);

	m_accountListProxyModel.setSourceModel(&m_accountSelectionModel);
	{
		QList<int> columnList;
		columnList.append(BackupSelectionModel::COL_USERNAME);
		m_accountListProxyModel.setFilterKeyColumns(columnList);
	}
	m_ui->accountTableView->setModel(&m_accountListProxyModel);

	m_ui->accountTableView->installEventFilter(
	    new (::std::nothrow) TableHomeEndFilter(m_ui->accountTableView));
	m_ui->accountTableView->installEventFilter(
	    new (::std::nothrow) TableSpaceSelectionFilter(
	        BackupSelectionModel::COL_MSGS_CHECKBOX, m_ui->accountTableView));
	m_ui->accountTableView->installEventFilter(
	    new (::std::nothrow) TableTabIgnoreFilter(m_ui->accountTableView));

	m_ui->accountTableView->setSquareColumnWidth(
	    BackupSelectionModel::COL_MSGS_CHECKBOX);
	m_ui->accountTableView->setColumnHidden(
	    BackupSelectionModel::COL_ACCOUNT_NAME, true);
	m_ui->accountTableView->setColumnHidden(
	    BackupSelectionModel::COL_BOX_ID, true);
	m_ui->accountTableView->setColumnWidth(2, 200);

	connect(&m_accountSelectionModel,
	    SIGNAL(dataChanged(QModelIndex,QModelIndex)),
	    this, SLOT(modifiedAccountSelection()));
}

DlgConvertImportFromMobileApp::~DlgConvertImportFromMobileApp(void)
{
	PrefsSpecific::setDlgTblRelColWidths(*GlobInstcs::prefsPtr,
	    dlgName, accountTableName,
	    Dimensions::relativeTableColumnWidths(m_ui->accountTableView));
	PrefsSpecific::setDlgTblColSortOrder(*GlobInstcs::prefsPtr,
	    dlgName, accountTableName,
	    Dimensions::tableColumnSortOrder(m_ui->accountTableView));

	delete m_ui;
}

void DlgConvertImportFromMobileApp::view(MainWindow *mw, QWidget *parent)
{
	if (Q_UNLIKELY(Q_NULLPTR == mw)) {
		Q_ASSERT(0);
		return;
	}

	DlgConvertImportFromMobileApp dlg(mw, parent);

	const QSize dfltSize = dlg.size();
	{
		const QSize newSize = Dimensions::dialogueSize(&dlg,
		    PrefsSpecific::dlgSize(*GlobInstcs::prefsPtr, dlgName),
		    dfltSize);
		if (newSize.isValid()) {
			dlg.resize(newSize);
		}
	}

	dlg.exec();

	PrefsSpecific::setDlgSize(*GlobInstcs::prefsPtr, dlgName,
	    dlg.size(), dfltSize);
}

void DlgConvertImportFromMobileApp::showEvent(QShowEvent *event)
{
	QDialog::showEvent(event);

	Dimensions::setRelativeTableColumnWidths(m_ui->accountTableView,
	    PrefsSpecific::dlgTblRelColWidths(*GlobInstcs::prefsPtr,
	    dlgName, accountTableName));
	Dimensions::setTableColumnSortOrder(m_ui->accountTableView,
	    PrefsSpecific::dlgTblColSortOrder(*GlobInstcs::prefsPtr,
	    dlgName, accountTableName));
}

void DlgConvertImportFromMobileApp::applyAllSelection(int state)
{
	m_accountSelectionModel.disconnect(
	    SIGNAL(dataChanged(QModelIndex, QModelIndex)),
	    this, SLOT(modifiedAccountSelection()));

	switch (state) {
	case Qt::Unchecked:
		m_ui->allCheckBox->setTristate(false);
		m_accountSelectionModel.setAllCheck(false);
		break;
	case Qt::PartiallyChecked:
		/* Do nothing. */
		break;
	case Qt::Checked:
		m_ui->allCheckBox->setTristate(false);
		m_accountSelectionModel.setAllCheck(true);
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	connect(&m_accountSelectionModel,
	    SIGNAL(dataChanged(QModelIndex,QModelIndex)),
	    this, SLOT(modifiedAccountSelection()));

	m_ui->importGroupBox->setEnabled(
	    m_accountSelectionModel.numChecked() > 0);
}

void DlgConvertImportFromMobileApp::filterAccount(const QString &text)
{
	/* Store style at first invocation. */
	if (m_dfltFilerLineStyleSheet == SortFilterProxyModel::invalidFilterEditStyle) {
		m_dfltFilerLineStyleSheet = m_ui->accountFilterLine->styleSheet();
	}

#if (QT_VERSION >= QT_VERSION_CHECK(5, 15, 0))
	m_accountListProxyModel.setFilterRegularExpression(
	    QRegularExpression(QRegularExpression::escape(text),
	        QRegularExpression::CaseInsensitiveOption));
#else /* < Qt-5.15 */
	m_accountListProxyModel.setFilterRegExp(QRegExp(text,
	    Qt::CaseInsensitive, QRegExp::FixedString));
#endif /* >= Qt-5.15 */
	if (text.isEmpty()) {
		m_ui->accountFilterLine->setStyleSheet(m_dfltFilerLineStyleSheet);
	} else if (m_accountListProxyModel.rowCount() != 0) {
		m_ui->accountFilterLine->setStyleSheet(
		    SortFilterProxyModel::foundFilterEditStyle);
	} else {
		m_ui->accountFilterLine->setStyleSheet(
		    SortFilterProxyModel::notFoundFilterEditStyle);
	}
}

void DlgConvertImportFromMobileApp::modifiedAccountSelection(void)
{
	enum Qt::CheckState state = Qt::PartiallyChecked;

	if ((m_accountSelectionModel.rowCount() == 0) ||
	     (m_accountSelectionModel.numChecked()
	     == m_accountSelectionModel.rowCount())) {
		state = Qt::Checked;
	} else if (m_accountSelectionModel.numChecked() == 0) {
		state = Qt::Unchecked;
	}

	m_ui->allCheckBox->setTristate(state == Qt::PartiallyChecked);
	m_ui->allCheckBox->setCheckState(state);

	m_ui->importGroupBox->setEnabled(
	    m_accountSelectionModel.numChecked() > 0);
}

void DlgConvertImportFromMobileApp::openZipPackage(void)
{
	/* Disable and clear some UI components and clear model. */
	m_ui->labelZipFileName->clear();
	m_ui->zipPackageInfoText->clear();
	m_ui->importInfoLabel->clear();
	m_ui->allCheckBox->setChecked(false);
	m_ui->acntSelectionGroupBox->setEnabled(false);
	m_ui->importGroupBox->setEnabled(false);
	m_accountSelectionModel.removeAllRows();

	/* Select and open ZIP package.*/
	m_zipPath = QFileDialog::getOpenFileName(this, tr("Open ZIP File"),
	    QString(), tr("ZIP file (*.zip)"));

	if (Q_UNLIKELY(m_zipPath.isEmpty())) {
		return;
	}
	QFileInfo fi(m_zipPath);
	m_ui->labelZipFileName->setText(tr("File: %1").arg(fi.fileName()));

	/* Load content of ZIP archive and find all message databases and ZFO. */
	QStringList foundMsgDbs;
	QString backupInfoText;
	bool canUnzip = LoadMobileZipPackage::loadBackupJsonFromZip(m_zipPath,
	    foundMsgDbs, backupInfoText);
	m_ui->zipPackageInfoText->setText(backupInfoText);
	if (Q_UNLIKELY(!canUnzip)) {
		return;
	}

	/* Get account ids from all message databases. */
	QList<AcntId> acntIdList =
	    DbConvertMobileToDesktop::getAcntIdsFromDbName(foundMsgDbs);
	if (Q_UNLIKELY(acntIdList.isEmpty())) {
		return;
	}

	/* Fill account model. */
	addAccountsIntoModel(m_accountSelectionModel, acntIdList);
	m_ui->acntSelectionGroupBox->setEnabled(true);
}

QStringList DlgConvertImportFromMobileApp::unZipSelectedAcnts(
    const QString &zipPath, const QString &unZipPath,
    const QList<AcntId> &selectedAcnts)
{
	if (Q_UNLIKELY(zipPath.isEmpty() || unZipPath.isEmpty())) {
		return QStringList();
	}

	/* Set progressbar to infinite loop. */
	m_ui->progressBar->setMinimum(0);
	m_ui->progressBar->setMaximum(0);
	m_ui->progressBar->setValue(0);
	m_ui->importInfoLabel->setText(tr("Unpacking archive: %1").arg(QFileInfo(zipPath).fileName()));
	QCoreApplication::processEvents();

	/* Unzip ZFO db, selected message databases and their ZFO files. */
	QStringList unzipedFileList;
	QString unZipInfoText(tr("All files have been unpacked successfully."));
	if (Q_UNLIKELY(!LoadMobileZipPackage::unZipSelectedFilesFromZiP(zipPath,
	    unZipPath, selectedAcnts, unzipedFileList))) {
		unZipInfoText = tr("No file has been unpacked.");
	}

	/* Disable infinite loop of progressbar. */
	m_ui->progressBar->setMaximum(1);
	m_ui->importInfoLabel->setText(unZipInfoText);

	return unzipedFileList;
}

void DlgConvertImportFromMobileApp::unzipAndImportMsgs(void)
{
	m_ui->importButton->setEnabled(false);

	QString infoText(tr("All data boxes have been imported."));
	QString unZipPath;
	QFileInfo fi(m_zipPath);
	QStringList dbfiles;

	/* Get list of checked account ids from account model. */
	const QList<AcntId> acntIdList = m_accountSelectionModel.checkedAcntIds();
	if (Q_UNLIKELY(acntIdList.count() <= 0)) {
		infoText = tr("No data boxes selected.");
		goto fail1;
	}

	/* Create temporary directory for unzip. */
	unZipPath = createSubDirPath(fi.absolutePath(), UNZIP_WORKING_DIR_NAME);
	if (Q_UNLIKELY(unZipPath.isEmpty())) {
		infoText =
		    tr("Cannot create temporary directory for extracted files.");
		goto fail1;
	}

	/* Unzip ZFO db, selected message databases and their ZFO files. */
	dbfiles = unZipSelectedAcnts(m_zipPath, unZipPath, acntIdList);
	if (Q_UNLIKELY(dbfiles.isEmpty())) {
		infoText = tr("No file has been extracted.");
		goto fail;
	}

	m_ui->importInfoLabel->setText(tr("Data for selected data boxes have been extracted."));

	if (!importAccountsFromDbs(dbfiles, unZipPath)) {
		m_ui->importInfoLabel->setText(tr("Some data boxes haven't been imported."));
	}

fail:
	/* Delete unzip working directory. */
	removeRecursivelyDir(unZipPath);
fail1:
	m_ui->importInfoLabel->setText(infoText);
	m_ui->importButton->setEnabled(true);
}

bool DlgConvertImportFromMobileApp::importAccountsFromDbs(
    const QStringList &dbPathList, const QString &unZipPath)
{
	if (Q_UNLIKELY(dbPathList.isEmpty() ||
	        GlobInstcs::acntMapPtr == Q_NULLPTR)) {
		return false;
	}

	const QList<AcntId> acntIdList =
	    DbConvertMobileToDesktop::getAcntIdsFromDbName(dbPathList);
	bool success = true;

	m_ui->progressBar->setValue(0);
	m_ui->progressBar->setMaximum(acntIdList.count());
	int pbValue = m_ui->progressBar->minimum();

	/* Create account or import message for all account ids. */
	for (const AcntId &acntId : acntIdList) {

		QStringList dbs = getAcntMsgDbs(acntId, dbPathList);

		m_ui->progressBar->setValue(pbValue++);
		QCoreApplication::processEvents();

		/* Check whether account already exists. */
		if (GlobInstcs::acntMapPtr->acntIds().contains(acntId)) {
			QMessageBox msgBox(this);
			msgBox.setIcon(QMessageBox::Question);
			msgBox.setWindowTitle(tr("Import data box: %1").arg(acntId.username()));
			msgBox.setText(tr("Data box with username '%1' already exists.").arg(acntId.username()));
			msgBox.setInformativeText(tr("Do you want to import messages from database into the data box?"));
			msgBox.setStandardButtons(QMessageBox::Yes|QMessageBox::No);
			msgBox.setDefaultButton(QMessageBox::No);
			if (msgBox.exec() == QMessageBox::No) {
				continue;
			}
			if (Q_UNLIKELY(!importDbMsgsIntoAcnt(m_mw, acntId, dbs, this))) {
				success = false;
			}

		} else {

			QString text(tr("Data box with name '%1' has been created (username '%1'). The database files have been set as the actual message databases for this data box.").arg(acntId.username()));
			QString errText;
			if (Q_UNLIKELY(!createAccountFromDb(acntId, dbs, unZipPath, errText))) {
				text = tr("Data box with username '%1' hasn't been created.").arg(acntId.username());
				success = false;
			}
			QMessageBox msgBox(this);
			msgBox.setIcon(QMessageBox::Information);
			msgBox.setWindowTitle(tr("Create data box: %1").arg(acntId.username()));
			msgBox.setText(text);
			msgBox.setInformativeText(errText);
			msgBox.exec();
		}
	}

	m_ui->progressBar->setValue(0);

	return success;
}

bool DlgConvertImportFromMobileApp::createAccountFromDb(const AcntId &acntId,
    const QStringList &dbPathList, const QString &unZipPath, QString &errTxt)
{
	const QString appDataLocation(GlobInstcs::iniPrefsPtr->confDir());

	/* Check if some database with username exists in the target location. */
	if (Q_UNLIKELY(isSomeDbFileAvailable(dbPathList, appDataLocation))) {
		errTxt = tr("Some message databases with username '%1' already exist in the target location '%2'. Move or delete them and try importing the data again.").arg(acntId.username()).arg(appDataLocation);
		return false;
	}

	/* Check available space for databases moving. */
	if (Q_UNLIKELY(!checkAvailableSpace(dbPathList, appDataLocation))) {
		errTxt = tr("No free space to move databases into the target location '%1'.").arg(appDataLocation);
		return false;
	}

	/* Move message databases and extern zfo files to application data location. */
	if (Q_UNLIKELY(!moveDbToAppDataLocation(unZipPath, dbPathList, appDataLocation))) {
		errTxt = tr("Cannot move databases into the target location '%1'.").arg(appDataLocation);
		return false;
	}

	/* Create and add account into account map. */
	if (Q_UNLIKELY(!createAccount(*GlobInstcs::acntMapPtr,
	        acntId, appDataLocation))) {
		errTxt = tr("Cannot add data box into application.");
		return false;
	}

	errTxt = tr("You'll have to modify the data box properties in order to log in to the ISDS server correctly.");
	return true;
}
