// SPDX-License-Identifier: GPL-2.0-or-later
// SPDX-FileCopyrightText: 2024 Konrad Twardowski

#include "commandline.h"
#include "config.h"
#include "fileshortcut.h"
#include "mainwindow.h"
#include "udialog.h"
#include "uwidgets.h"
#include "triggers/datetime.h"

#include <QAbstractItemView>
#include <QButtonGroup>
#include <QFormLayout>
#include <QPushButton>
#include <QScreen>
#include <QStandardPaths>

// public:

FileShortcutDialog::FileShortcutDialog(QWidget *parent) :
	QWizard(parent) {

	setButtonText(FinishButton, i18n("Create"));
	setSizeGripEnabled(true);
	setWindowTitle(i18n("Create File Shortcut"));

	auto *mainWindow = MainWindow::self();
	m_action = mainWindow->getSelectedAction();
	m_trigger = mainWindow->getSelectedTrigger();

	m_triggerOptionSupported =
		(m_trigger->id() != "file-monitor") &&
		(m_trigger->id() != "process-monitor");

	createActionPage();
	createTriggerPage();
	createNamePage();
	createAdvancedPage();

	auto updateView = [this]() {
// TODO: cleanup UI
		m_selectedTrigger->setEnabled(m_selectedAction->isChecked() && m_triggerOptionSupported);
		m_noTrigger->setEnabled(m_selectedAction->isChecked());

		QString commandLine = getCommandLine();
		m_commandLine->setPlainText(commandLine);
		m_file->setText(QString::fromStdString(getFile().string()));

		qDebug() << "FileShortcutDialog::updateView()" << commandLine;
	};

	connect(m_selectedAction, &QRadioButton::clicked, updateView);
	connect(m_noAction, &QRadioButton::clicked, updateView);

	connect(m_selectedTrigger, &QRadioButton::clicked, updateView);
	connect(m_noTrigger, &QRadioButton::clicked, updateView);

	#if QT_VERSION < QT_VERSION_CHECK(6, 9, 0)
	connect(m_confirm, &QCheckBox::stateChanged, updateView);
	connect(m_init, &QCheckBox::stateChanged, updateView);
	connect(m_hideUI, &QCheckBox::stateChanged, updateView);
	#else
	connect(m_confirm, &QCheckBox::checkStateChanged, updateView);
	connect(m_init, &QCheckBox::checkStateChanged, updateView);
	connect(m_hideUI, &QCheckBox::checkStateChanged, updateView);
	#endif // QT_VERSION

	connect(m_location, &QComboBox::activated, updateView);
	connect(m_scale, &QComboBox::activated, updateView);

	connect(m_name, &QLineEdit::textChanged, updateView);

	connect(this, &QDialog::accepted, [this]() { createFile(); });

	updateView();
}

bool FileShortcutDialog::validateCurrentPage() {
	if (currentPage() == m_namePage) {
		UPath file = getFile();

		if (std::filesystem::exists(file)) {
			UMessageBuilder message(UMessageBuilder::Type::Question);
			message.okText(i18n("Overwrite"));
			message.plainText(
				i18n("File already exists") + "\n" +
				"\n" +
				QString::fromStdString(file.string())
			);

			if (! message.exec(this))
				return false;
		}
	}

	return QWizard::validateCurrentPage();
}

// private:

void FileShortcutDialog::createActionPage() {
	m_selectedAction = new QRadioButton(Utils::makeTitle(
		i18n("Selected Action"),
		m_action->originalText()
	));
	m_selectedAction->setChecked(true);
	m_selectedAction->setToolTip(i18n("Initialized from the main window"));

	m_noAction = new QRadioButton(i18n("None"));

	auto *group = new QButtonGroup(this);
	group->addButton(m_selectedAction);
	group->addButton(m_noAction);

	auto *page = new QWizardPage();
	page->setTitle(i18n("Action"));
	addPage(page);

	auto *l = Utils::newFormLayout(page);
	l->addWidget(m_selectedAction);
	l->addWidget(m_noAction);
}

void FileShortcutDialog::createAdvancedPage() {
	m_confirm = new QCheckBox(CLI::confirmOption().description());
	m_confirm->setChecked(Config::confirmAction.getBool());

	m_init = new QCheckBox(CLI::initOption().description());
	m_hideUI = new QCheckBox(CLI::hideUIOption().description());

	m_scale = new QComboBox();
	m_scale->view()->setAlternatingRowColors(true);
	m_scale->addItem(Utils::makeTitle(i18n("Default"), QString("%0x").arg(QApplication::primaryScreen()->devicePixelRatio())));
	m_scale->addItem(Utils::makeTitle(i18n("Medium"), "1.5x"));
	m_scale->addItem(Utils::makeTitle(i18n("Large"), "2x"));

	m_commandLine = Utils::newTextEdit("", QPlainTextEdit::WidgetWidth);
	m_commandLine->setFixedHeight(100_px);
	m_commandLine->setReadOnly(true);

	auto *page = new QWizardPage();
	page->setTitle(i18n("Advanced"));
	addPage(page);

	auto *l = Utils::newFormLayout(page);
	l->addRow(m_confirm);
	l->addRow(m_init);
	l->addRow(m_hideUI);
	l->addRow(i18n("User Interface scale"), m_scale);
// FIXME: breaks layout without stretch
	l->addRow(i18n("Command Line:"), m_commandLine);
}

void FileShortcutDialog::createNamePage() {
	m_name = new QLineEdit(QApplication::applicationDisplayName());
	m_name->setClearButtonEnabled(true);

	m_location = new QComboBox();
	m_location->view()->setAlternatingRowColors(true);

	QIcon alignFallbackIcon = UWidgets::isIconThemeSupported() ? UWidgets::emptyIcon(m_location->iconSize()) : QIcon();

	m_location->addItem(
		QIcon::fromTheme("system-run", alignFallbackIcon),
		Utils::makeTitle(i18n("Autostart"), i18n("Start Minimized"))
	);

	m_location->addItem(
		QIcon::fromTheme("system-run", alignFallbackIcon),
		i18n("Autostart")
	);

	// HACK: KDE Breeze does not follow https://specifications.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html
	m_location->addItem(
		QIcon::fromTheme(Utils::isKDE() ? "start-here-kde" : "start-here", alignFallbackIcon),
		i18n("Menu")
	);

	m_location->addItem(
		QIcon::fromTheme("user-desktop", alignFallbackIcon),
		i18n("Desktop")
	);

	m_file = new QLineEdit();
	m_file->setReadOnly(true);

	m_namePage = new QWizardPage();
	m_namePage->setTitle(i18n("Name and Location"));
	addPage(m_namePage);

	auto *l = Utils::newFormLayout(m_namePage);
	l->addRow(i18n("Name:"), m_name);

	auto *locationOpenButton = new QPushButton(i18n("Open"));
	connect(locationOpenButton, &QPushButton::clicked, [this]() {
		Utils::openInExternalApp(getLocationDir());
	});

	auto *locationLayout = UWidgets::newHBoxLayout({ m_location, locationOpenButton }, 10_px);

	l->addRow(i18n("Location:"), locationLayout);

	l->addRow(i18n("File:"), m_file);
}

void FileShortcutDialog::createTriggerPage() {
	m_selectedTrigger = new QRadioButton(Utils::makeTitle(
		i18n("Selected Time/Event"),
		Utils::makeTitle(m_trigger->text(), "")//!!!m_trigger->getStringOption())
	));

	m_selectedTrigger->setToolTip(i18n("Initialized from the main window"));

	m_noTrigger = new QRadioButton(i18n("No Delay"));

	if (m_triggerOptionSupported) {
		m_selectedTrigger->setChecked(true);
	}
	else {
		m_selectedTrigger->setEnabled(false);
		m_noTrigger->setChecked(true);
	}

	auto *group = new QButtonGroup(this);
	group->addButton(m_selectedTrigger);
	group->addButton(m_noTrigger);

	auto *page = new QWizardPage();
	page->setTitle(i18n("Time/Event"));
	addPage(page);

	auto *l = Utils::newFormLayout(page);
	l->addWidget(m_selectedTrigger);
	l->addWidget(m_noTrigger);
}

QString FileShortcutDialog::getCommandLine() {
	QStringList result;

	#ifdef Q_OS_LINUX
	QFileInfo appImage = Utils::getAppImageInfo();
	if (appImage.isFile())
		result += appImage.filePath();
	else
		result += QApplication::applicationFilePath();
	#else
	result += QApplication::applicationFilePath();
	#endif // Q_OS_LINUX

	if (m_confirm->isChecked())
		result += CLI::formatName(CLI::confirmOption());

	if (m_hideUI->isChecked())
		result += CLI::formatName(CLI::hideUIOption());

	if (
		m_init->isChecked() ||
		(static_cast<LocationType>(m_location->currentIndex()) == LocationType::AUTOSTART_MINIMIZED)
	) {
		result += CLI::formatName(CLI::initOption());
	}

	switch (static_cast<ScaleType>(m_scale->currentIndex())) {
		case ScaleType::DEFAULT:
			// no arg
			break;
		case ScaleType::X15:
			result += CLI::formatName(CLI::scaleOption(), "1.5");
			break;
		case ScaleType::X2:
			result += CLI::formatName(CLI::scaleOption(), "2");;
			break;
	}

	if (m_selectedAction->isChecked()) {
		if (auto option = m_action->commandLineOption())
			result += CLI::formatName(option.value());

		if (m_selectedTrigger->isChecked()) {
// FIXME: day of week
			if (m_trigger->id() == "date-time") {
				auto base = static_cast<DateTimeTriggerBase *>(m_trigger);
				result += TimeOption::formatDateTime(base->getSelectedDateTime());
			}
			else if (m_trigger->id() == "idle-monitor") {
				auto base = static_cast<DateTimeTriggerBase *>(m_trigger);
				result += "--idle";
				result += TimeOption::formatTime(base->getSelectedTime());
			}
			else if (m_trigger->id() == "time-from-now") {
				auto base = static_cast<DateTimeTriggerBase *>(m_trigger);
				result += TimeOption::formatTime(base->getSelectedTime());
			}
		}
	}

	return result.join(" ");
}

void FileShortcutDialog::createFile() {
	QString commandLine = getCommandLine();
	UPath file = getFile();
	qDebug() << "FileShortcutDialog::createFile()" << QString::fromStdString(file.string()) << commandLine;//!!!

	#ifdef Q_OS_LINUX
	auto entry = Config::createDesktopEntry(file);
	entry->setValue("Exec", commandLine);
	entry->setValue("Icon", "kshutdown");
	entry->setValue("Name", m_name->text().trimmed());
	entry->sync();

	if (static_cast<LocationType>(m_location->currentIndex()) == LocationType::DESKTOP) {
		auto status = std::filesystem::status(file);
		std::filesystem::permissions(file, status.permissions() | std::filesystem::perms::owner_exec);
	}
	#endif // Q_OS_LINUX
}

UPath FileShortcutDialog::getFile() {
	QString name = m_name->text();

	// DOC: https://learn.microsoft.com/en-us/windows/win32/fileio/naming-a-file
	for (char i : { '<', '>', ':', '"', '/', '\\', '|', '?', '*' })
		name.remove(i);

	name = name.trimmed();

	if (name.isEmpty())
		name = QApplication::applicationDisplayName();

	#ifdef Q_OS_LINUX
	name += ".desktop";
	#endif // Q_OS_LINUX

	return getLocationDir() / name.toStdString();
}

UPath FileShortcutDialog::getLocationDir() {
// TODO: use filesystemPath function, etc. #qt6
	QString location;

	switch (static_cast<LocationType>(m_location->currentIndex())) {
		case LocationType::AUTOSTART:
		case LocationType::AUTOSTART_MINIMIZED: {
			#ifdef Q_OS_LINUX
			return Utils::getAutostartDir(true);
			#endif // Q_OS_LINUX
		} break;
		case LocationType::MENU:
			location = QStandardPaths::writableLocation(QStandardPaths::ApplicationsLocation);
			break;
		case LocationType::DESKTOP:
			location = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
			break;
	}

	UPath result = location.toStdString();

	if (std::filesystem::exists(result))
		return result;

	UPath homeDir = QDir::home().path().toStdString();

	return homeDir;
}
