/*
 * 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 <QDir>
#include <QFileDialog>
#include <QMessageBox>
#include <QString>

#include "src/datovka_shared/identifiers/account_id.h"
#include "src/datovka_shared/log/log.h"
#include "src/dimensions/dimensions.h"
#include "src/global.h"
#include "src/gui/dlg_change_directory.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 "ui_dlg_change_directory.h"

#define macroDfltDbDir() \
    GlobInstcs::iniPrefsPtr->confDir()

DlgChangeDirectory::DlgChangeDirectory(const AcntId &acntId,
    QWidget *parent)
    : QDialog(parent),
    m_ui(new (::std::nothrow) Ui::DlgChangeDirectory),
    m_acntId(acntId)
{
	m_ui->setupUi(this);
	/* Tab order is defined in UI file. */

	{
		const AcntData itemSettings(GlobInstcs::acntMapPtr->acntData(m_acntId));
		m_ui->accoutLabel->setText(QString("%1 (%2)")
		    .arg(itemSettings.accountName()).arg(itemSettings.userName()));
	}

	QString currentDir;
	{
		/* Get current settings. */
		const AcntData itemSettings(GlobInstcs::acntMapPtr->acntData(m_acntId));

		currentDir = itemSettings.dbDir();
		if (currentDir.isEmpty()) {
			/* Set default directory name. */
			currentDir = macroDfltDbDir();
		}
	}

	m_ui->currentPathLineEdit->setText(QDir::toNativeSeparators(currentDir));

	m_ui->moveDataRadioButton->setChecked(true);
	m_ui->useDataRadioButtonEraseSrc->setEnabled(false);
	m_ui->useDataRadioButtonEraseSrc->setVisible(false);
	m_ui->useDataRadioButtonLeaveSrc->setEnabled(false);
	m_ui->useDataRadioButtonLeaveSrc->setVisible(false);

	const bool isCurrentDefault = QDir::fromNativeSeparators(currentDir) == QDir::fromNativeSeparators(macroDfltDbDir());
	m_ui->useDefaultLabel->setVisible(!isCurrentDefault);
	m_ui->useDefaultButton->setVisible(!isCurrentDefault);

	m_ui->newPathLineEdit->setText(QString());

	connect(m_ui->useDefaultButton, SIGNAL(clicked()),
	    this, SLOT(useDefaultDirectory(void)));
	connect(m_ui->chooseButton, SIGNAL(clicked()),
	    this, SLOT(chooseNewDirectory(void)));

	connect(m_ui->newPathLineEdit, SIGNAL(textChanged(QString)),
	    this, SLOT(updateContent(void)));

	connect(m_ui->startDataRadioButtonEraseSrc, SIGNAL(toggled(bool)),
	    this, SLOT(eraseSrcWarning(bool)));
	connect(m_ui->useDataRadioButtonEraseSrc, SIGNAL(toggled(bool)),
	    this, SLOT(eraseSrcWarning(bool)));

	updateContent();
}

DlgChangeDirectory::~DlgChangeDirectory(void)
{
	delete m_ui;
}

bool DlgChangeDirectory::changeDataDirectory(const AcntId &acntId,
    MessageDbSet *dbSet, QWidget *parent)
{
	if (Q_UNLIKELY((!acntId.isValid()) || (dbSet == Q_NULLPTR))) {
		Q_ASSERT(0);
		return false;
	}

	enum Action action = ACT_NONE;
	QString newDirPath;

	/* Get current settings. */
	const AcntData itemSettings(GlobInstcs::acntMapPtr->acntData(acntId));

	QString oldDbDir(itemSettings.dbDir());
	if (oldDbDir.isEmpty()) {
		/* Set default directory name. */
		oldDbDir = macroDfltDbDir();
	}

	if (!chooseAction(acntId, newDirPath, action, parent)) {
		return false;
	}

	if (Q_UNLIKELY(newDirPath.isEmpty())) {
		Q_ASSERT(0);
		return false;
	}

	return relocateDatabase(acntId, dbSet, oldDbDir, newDirPath, action,
	    parent);
}

void DlgChangeDirectory::useDefaultDirectory(void)
{
	m_ui->newPathLineEdit->setText(QDir::toNativeSeparators(macroDfltDbDir()));
}

void DlgChangeDirectory::chooseNewDirectory(void)
{
	QString newDir(QFileDialog::getExistingDirectory(this,
	    tr("Open Directory"), QDir::fromNativeSeparators(m_ui->newPathLineEdit->text()),
	    QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks));

	if (!newDir.isEmpty()) {
		m_ui->newPathLineEdit->setText(QDir::toNativeSeparators(newDir));
	}
}

void DlgChangeDirectory::eraseSrcWarning(bool checked)
{
	if (!checked) {
		return;
	}

	const QRadioButton *senderButton = qobject_cast<QRadioButton *>(QObject::sender());
	if (Q_UNLIKELY(Q_NULLPTR == senderButton)) {
		return;
	}

	const AcntData itemSettings(GlobInstcs::acntMapPtr->acntData(m_acntId));

	QMessageBox::StandardButton ret = QMessageBox::warning(this,
	    tr("Erase Original Data?"),
	    tr("You've selected: %1\n\nThe selected operation will erase message data used at this moment in the data box '%2 (%3)'.\n\nDo you want keep this selection?")
	        .arg(senderButton->text())
	        .arg(itemSettings.accountName())
	        .arg(itemSettings.userName()),
	    QMessageBox::Yes | QMessageBox::No, QMessageBox::No);

	if (QMessageBox::No == ret) {
		/* Select same operation but leave current data. */
		if (senderButton == m_ui->startDataRadioButtonEraseSrc) {
			m_ui->startDataRadioButtonLeaveSrc->setChecked(true);
		} else if (senderButton == m_ui->useDataRadioButtonEraseSrc) {
			m_ui->useDataRadioButtonLeaveSrc->setChecked(true);
		} else {
			/* Use default. */
			m_ui->moveDataRadioButton->setChecked(true);
		}
	}
}

void DlgChangeDirectory::updateContent(void)
{
	QString newPath = m_ui->newPathLineEdit->text();
	if (!newPath.isEmpty()) {
		newPath = QDir::fromNativeSeparators(newPath);
	}

	{
		const bool isNewtDefault = newPath == QDir::fromNativeSeparators(macroDfltDbDir());
		m_ui->useDefaultButton->setEnabled(!isNewtDefault);
	}

	if (Q_UNLIKELY(newPath.isEmpty())) {
		m_ui->labelWarning->hide();
		m_ui->labelWarning->setStyleSheet(QString());
		m_ui->labelWarning->setText(QString());
		m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);

		return;
	}

	if (Q_UNLIKELY(newPath == QDir::fromNativeSeparators(m_ui->currentPathLineEdit->text()))) {
		m_ui->labelWarning->show();
		m_ui->labelWarning->setStyleSheet("QLabel { color: red }");
		m_ui->labelWarning->setText(tr("You cannot use the original directory as destination."));
		m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(false);

		return;
	}

	bool targetHasDb = false;

	QString fileNameStr;
	const enum AccountInteraction::AccessStatus as =
	    AccountInteraction::checkDbSetLocation(m_acntId, newPath, fileNameStr);
	switch (as) {
	case AccountInteraction::AS_DB_ALREADY_PRESENT:
		m_ui->labelWarning->show();
		m_ui->labelWarning->setStyleSheet(QString());
		m_ui->labelWarning->setText(tr("The target directory seems to contain a database."));

		targetHasDb = true;
		break;
	case AccountInteraction::AS_DB_NOT_PRESENT:
		m_ui->labelWarning->hide();
		m_ui->labelWarning->setStyleSheet(QString());
		m_ui->labelWarning->setText(QString());
		break;
	case AccountInteraction::AS_ERR:
		m_ui->labelWarning->show();
		m_ui->labelWarning->setStyleSheet("QLabel { color: red }");
		m_ui->labelWarning->setText(tr("The target directory seems to contain some conflicting data that may cause problems."));
		break;
	default:
		m_ui->labelWarning->show();
		m_ui->labelWarning->setStyleSheet("QLabel { color: red }");
		m_ui->labelWarning->setText(tr("There seems to be an unspecified problem with the target directory."));
		break;
	}
	m_ui->buttonBox->button(QDialogButtonBox::Ok)->setEnabled(true);

	bool useWasSelected = m_ui->useDataRadioButtonEraseSrc->isChecked()
	    || m_ui->useDataRadioButtonLeaveSrc->isChecked();

	m_ui->useDataRadioButtonEraseSrc->setEnabled(targetHasDb);
	m_ui->useDataRadioButtonEraseSrc->setVisible(targetHasDb);
	m_ui->useDataRadioButtonLeaveSrc->setEnabled(targetHasDb);
	m_ui->useDataRadioButtonLeaveSrc->setVisible(targetHasDb);

	/*
	 * Select to move database when previous selection was to use existent
	 * new one but the radio buttons are disabled now.
	 */
	if ((!targetHasDb) && useWasSelected) {
		m_ui->moveDataRadioButton->setChecked(true);
	}
}

bool DlgChangeDirectory::chooseAction(const AcntId &acntId,
    QString &newDir, enum Action &action, QWidget *parent)
{
	DlgChangeDirectory dlg(acntId, parent);

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

	int ret = dlg.exec();

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

	if (QDialog::Accepted == ret) {
		newDir = QDir::fromNativeSeparators(dlg.m_ui->newPathLineEdit->text());
		if (dlg.m_ui->moveDataRadioButton->isChecked()) {
			action = ACT_MOVE;
		} else if (dlg.m_ui->copyDataRadioButton->isChecked()) {
			action = ACT_COPY;
		} else if (dlg.m_ui->startDataRadioButtonEraseSrc->isChecked()) {
			action = ACT_NEW_ERASE_OLD;
		} else if (dlg.m_ui->startDataRadioButtonLeaveSrc->isChecked()) {
			action = ACT_NEW_LEAVE_OLD;
		} else if (dlg.m_ui->useDataRadioButtonEraseSrc->isChecked()) {
			action = ACT_USE_ERASE_OLD;
		} else if (dlg.m_ui->useDataRadioButtonLeaveSrc->isChecked()) {
			action = ACT_USE_LEAVE_OLD;
		}
		return true;
	} else {
		return false;
	}
}

bool DlgChangeDirectory::relocateDatabase(const AcntId &acntId,
    MessageDbSet *dbSet, const QString &oldDir, const QString &newDir,
    enum Action action, QWidget *parent)
{
	if (Q_UNLIKELY((!acntId.isValid()) || (dbSet == Q_NULLPTR))) {
		Q_ASSERT(0);
		return false;
	}

	/* Get current settings. */
	AcntData itemSettings(GlobInstcs::acntMapPtr->acntData(acntId));

	switch (action) {
	case ACT_MOVE:
		/* Move account database into new directory. */
		if (dbSet->moveToLocation(newDir)) {
			itemSettings.setDbDir(newDir, macroDfltDbDir());

			logInfo("Database files for '%s' have been moved from '%s' to '%s'.\n",
			    acntId.username().toUtf8().constData(),
			    QDir::toNativeSeparators(oldDir).toUtf8().constData(),
			    QDir::toNativeSeparators(newDir).toUtf8().constData());

			QMessageBox::information(parent,
			    tr("Change data directory for current data box"),
			    tr("Database files for '%1' have been successfully moved to\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);

			GlobInstcs::acntMapPtr->updateAccount(itemSettings);
			return true;
		} else {
			QMessageBox::critical(parent,
			    tr("Change data directory for current data box"),
			    tr("Database files for '%1' could not be moved to\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);
		}
		break;
	case ACT_COPY:
		/* Copy account database into new directory. */
		if (dbSet->copyToLocation(newDir)) {
			itemSettings.setDbDir(newDir, macroDfltDbDir());

			logInfo("Database files for '%s' have been copied from '%s' to '%s'.\n",
			    acntId.username().toUtf8().constData(),
			    QDir::toNativeSeparators(oldDir).toUtf8().constData(),
			    QDir::toNativeSeparators(newDir).toUtf8().constData());

			QMessageBox::information(parent,
			    tr("Change data directory for current data box"),
			    tr("Database files for '%1' have been successfully copied to\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);

			GlobInstcs::acntMapPtr->updateAccount(itemSettings);
			return true;
		} else {
			QMessageBox::critical(parent,
			    tr("Change data directory for current data box"),
			    tr("Database files for '%1' could not be copied to\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);
		}
		break;
	case ACT_NEW_ERASE_OLD:
		/* Create a new account database into new directory. */
		if (dbSet->reopenLocation(newDir, MessageDbSet::DO_YEARLY,
		        MessageDbSet::CM_CREATE_EMPTY_CURRENT,
		        MessageDbSet::AM_DELETE)) {
			itemSettings.setDbDir(newDir, macroDfltDbDir());

			logInfo("Database files for '%s' have been created in '%s'. Old database files have been deleted.\n",
			    acntId.username().toUtf8().constData(),
			    QDir::toNativeSeparators(newDir).toUtf8().constData());

			QMessageBox::information(parent,
			    tr("Change data directory for current data box"),
			    tr("New database files for '%1' have been successfully created in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir))
			        + "\n\n" + tr("Old database files have been deleted."),
			    QMessageBox::Ok);

			GlobInstcs::acntMapPtr->updateAccount(itemSettings);
			return true;
		} else {
			QMessageBox::critical(parent,
			    tr("Change data directory for current data box"),
			    tr("New database files for '%1' could not be created in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);
		}
		break;
	case ACT_NEW_LEAVE_OLD:
		/* Create a new account database into new directory. */
		if (dbSet->reopenLocation(newDir, MessageDbSet::DO_YEARLY,
		        MessageDbSet::CM_CREATE_EMPTY_CURRENT,
		        MessageDbSet::AM_LEAVE)) {
			itemSettings.setDbDir(newDir, macroDfltDbDir());

			logInfo("Database files for '%s' have been created in '%s'.\n",
			    acntId.username().toUtf8().constData(),
			    QDir::toNativeSeparators(newDir).toUtf8().constData());

			QMessageBox::information(parent,
			    tr("Change data directory for current data box"),
			    tr("New database files for '%1' have been successfully created in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);

			GlobInstcs::acntMapPtr->updateAccount(itemSettings);
			return true;
		} else {
			QMessageBox::critical(parent,
			    tr("Change data directory for current data box"),
			    tr("New database files for '%1' could not be created in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);
		}
		break;
	case ACT_USE_ERASE_OLD:
		if (dbSet->openLocation(newDir, MessageDbSet::DO_UNKNOWN,
		        MessageDbSet::CM_MUST_EXIST, MessageDbSet::AM_DELETE)) {
			itemSettings.setDbDir(newDir, macroDfltDbDir());

			logInfo("Database files for '%s' have been opened in '%s'. Old database files have been deleted.\n",
			    acntId.username().toUtf8().constData(),
			    QDir::toNativeSeparators(newDir).toUtf8().constData());

			QMessageBox::information(parent,
			    tr("Change data directory for current data box"),
			    tr("Existent database files for '%1' have been successfully opened in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir))
			        + "\n\n" + tr("Old database files have been deleted."),
			    QMessageBox::Ok);

			GlobInstcs::acntMapPtr->updateAccount(itemSettings);
			return true;
		} else {
			QMessageBox::critical(parent,
			    tr("Change data directory for current data box"),
			    tr("Existent database files for '%1' could not be opened in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);
		}
		break;
	case ACT_USE_LEAVE_OLD:
		if (dbSet->openLocation(newDir, MessageDbSet::DO_UNKNOWN,
		        MessageDbSet::CM_MUST_EXIST, MessageDbSet::AM_LEAVE)) {
			itemSettings.setDbDir(newDir, macroDfltDbDir());

			logInfo("Database files for '%s' have been opened in '%s'.\n",
			    acntId.username().toUtf8().constData(),
			    QDir::toNativeSeparators(newDir).toUtf8().constData());

			QMessageBox::information(parent,
			    tr("Change data directory for current data box"),
			    tr("Existent database files for '%1' have been successfully opened in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);

			GlobInstcs::acntMapPtr->updateAccount(itemSettings);
			return true;
		} else {
			QMessageBox::critical(parent,
			    tr("Change data directory for current data box"),
			    tr("Existent database files for '%1' could not be opened in\n\n'%2'.")
			        .arg(acntId.username()).arg(QDir::toNativeSeparators(newDir)),
			    QMessageBox::Ok);
		}
		break;
	default:
		Q_ASSERT(0);
		break;
	}

	return false;
}
