#include "CustomDataDialog.h"
#include "ui_CustomDataDialog.h"

#include "mission/missionparse.h"
#include "mission/util.h"

#include <ui/util/SignalBlockers.h>

#include <QHeaderView>
#include <QItemSelectionModel>
#include <QMessageBox>

namespace fso::fred::dialogs {

namespace {
enum Columns {
	ColKey = 0,
	ColValue = 1,
	ColumnCount = 2
};
} // namespace

CustomDataDialog::CustomDataDialog(QWidget* parent, EditorViewport* viewport)
	: QDialog(parent), ui(new Ui::CustomDataDialog()), _model(new CustomDataDialogModel(this, viewport)),
	  _viewport(viewport)
{
	ui->setupUi(this);

	buildView();
	refreshTable();

	// Initial selection if any rows exist
	if (_tableModel->rowCount() > 0) {
		selectRow(0);
	}
}

CustomDataDialog::~CustomDataDialog() = default;

void CustomDataDialog::accept()
{
	// If apply() returns true, close the dialog
	if (_model->apply()) {
		QDialog::accept();
	}
	// else: validation failed, don't close
}

void CustomDataDialog::reject()
{
	// Custom reject or close logic because we need to handle talkback to Mission Specs
	if (_model->query_modified()) {
		auto button = _viewport->dialogProvider->showButtonDialog(fso::fred::DialogType::Question,
			"Changes detected",
			"Do you want to keep your changes?",
			{fso::fred::DialogButton::Yes, fso::fred::DialogButton::No, fso::fred::DialogButton::Cancel});

		if (button == fso::fred::DialogButton::Yes) {
			accept();
		}
		if (button == fso::fred::DialogButton::No) {
			_model->reject();
			QDialog::reject(); // actually close
		}
	} else {
		_model->reject();
		QDialog::reject();
	}
}

void CustomDataDialog::closeEvent(QCloseEvent* e)
{
	reject();
	e->ignore(); // Don't let the base class close the window
}

void CustomDataDialog::setInitial(const SCP_map<SCP_string, SCP_string>& items)
{
	_model->setInitial(items);

	// Rebuild view from the new working copy
	refreshTable();
	if (_tableModel->rowCount() > 0) {
		selectRow(0);
	} else {
		clearEditors();
	}
}

void CustomDataDialog::buildView()
{
	_tableModel = new QStandardItemModel(this);
	_tableModel->setColumnCount(ColumnCount);
	_tableModel->setHorizontalHeaderLabels({tr("Key"), tr("Value")});

	ui->stringsTableView->setModel(_tableModel);
	ui->stringsTableView->setSelectionBehavior(QAbstractItemView::SelectRows);
	ui->stringsTableView->setSelectionMode(QAbstractItemView::SingleSelection);
	ui->stringsTableView->setEditTriggers(QAbstractItemView::NoEditTriggers);
	ui->stringsTableView->verticalHeader()->setVisible(false);
	ui->stringsTableView->horizontalHeader()->setStretchLastSection(true);
	ui->stringsTableView->setSortingEnabled(false);

	// Make sure headers are not interactive
	auto* hdr = ui->stringsTableView->horizontalHeader();
	hdr->setSectionsClickable(false);  // no click/press behavior
	hdr->setSortIndicatorShown(false); // hide sort arrow
	hdr->setHighlightSections(false);  // don't change look when selected
	hdr->setSectionsMovable(false);    // no drag-to-reorder columns
	hdr->setFocusPolicy(Qt::NoFocus);

	// When a selection is made then load the editors
	connect(ui->stringsTableView->selectionModel(),
		&QItemSelectionModel::selectionChanged,
		this,
		[this](const QItemSelection&, const QItemSelection&) {
			const auto idx = ui->stringsTableView->currentIndex();
			loadRowIntoEditors(idx.isValid() ? idx.row() : -1);
		});
}

void CustomDataDialog::refreshTable()
{
	_tableModel->setRowCount(0);

	const auto& rows = _model->items();
	_tableModel->insertRows(0, static_cast<int>(rows.size()));
	int r = 0;
	for (const auto& kv : rows) {
		auto* keyItem = new QStandardItem(QString::fromStdString(kv.first));
		auto* valItem = new QStandardItem(QString::fromStdString(kv.second));
		keyItem->setEditable(false);
		valItem->setEditable(false);
		_tableModel->setItem(r, ColKey, keyItem);
		_tableModel->setItem(r, ColValue, valItem);
		++r;
	}
}

void CustomDataDialog::selectRow(int row)
{
	if (row < 0 || row >= _tableModel->rowCount()) {
		ui->stringsTableView->clearSelection();
		loadRowIntoEditors(-1);
		return;
	}
	ui->stringsTableView->selectRow(row);
	loadRowIntoEditors(row);
}

void CustomDataDialog::loadRowIntoEditors(int row)
{
	util::SignalBlockers blockers(this);

	if (row < 0 || row >= _tableModel->rowCount()) {
		clearEditors();
		return;
	}

	const auto* keyItem = _tableModel->item(row, ColKey);
	const auto* valItem = _tableModel->item(row, ColValue);
	const auto key = keyItem ? keyItem->text() : QString();
	ui->keyLineEdit->setText(key);
	ui->valueLineEdit->setText(valItem ? valItem->text() : QString());
	updateHelpTextForKey(key);
}

void CustomDataDialog::updateHelpTextForKey(const QString& key)
{
	auto helpText = tr("No help text provided");
	for (const auto& entry : Default_custom_data) {
		if (key == QString::fromStdString(entry.key)) {
			helpText = QString::fromStdString(entry.description);
			break;
		}
	}

	ui->helpTextBrowser->setPlainText(helpText);
}

std::pair<SCP_string, SCP_string> CustomDataDialog::editorsToEntry() const
{
	std::pair<SCP_string, SCP_string> e;
	e.first = ui->keyLineEdit->text().toUtf8().constData();
	e.second = ui->valueLineEdit->text().toUtf8().constData();
	return e;
}

void CustomDataDialog::clearEditors()
{
	util::SignalBlockers blockers(this);

	ui->keyLineEdit->clear();
	ui->valueLineEdit->clear();
	ui->helpTextBrowser->setPlainText("");
}

void CustomDataDialog::on_addButton_clicked()
{
	auto e = editorsToEntry();
	SCP_string err;
	if (!_model->add(e, &err)) {
		QMessageBox::warning(this, tr("Invalid Entry"), QString::fromStdString(err));
		return;
	}

	refreshTable();
	selectRow(_tableModel->rowCount() - 1);
	clearEditors();
}

void CustomDataDialog::on_updateButton_clicked()
{
	const auto idx = ui->stringsTableView->currentIndex();
	if (!idx.isValid())
		return;

	auto e = editorsToEntry();
	SCP_string err;
	if (!_model->updateAt(static_cast<size_t>(idx.row()), e, &err)) {
		QMessageBox::warning(this, tr("Invalid Entry"), QString::fromStdString(err));
		return;
	}

	// The key change may reorder the map. Rebuild and reselect by key.
	refreshTable();
	if (auto idxOpt = _model->indexOfKey(e.first)) {
		selectRow(static_cast<int>(*idxOpt));
	}
}

void CustomDataDialog::on_removeButton_clicked()
{
	const auto idx = ui->stringsTableView->currentIndex();
	if (!idx.isValid())
		return;

	const auto key = _tableModel->item(idx.row(), ColKey)->text();
	if (QMessageBox::question(this, tr("Remove Entry"), tr("Remove key \"%1\"?").arg(key)) != QMessageBox::Yes) {
		return;
	}

	if (_model->removeAt(static_cast<size_t>(idx.row()))) {
		const int next = std::min(idx.row(), _tableModel->rowCount() - 2); // -1 after removal, then clamp
		refreshTable();
		selectRow(next);
	}
}

void CustomDataDialog::on_okAndCancelButtons_accepted()
{
	accept(); // Mission Specs will read model->items() and commit during its own Apply
}

void CustomDataDialog::on_okAndCancelButtons_rejected()
{
	reject();
}

} // namespace fso::fred::dialogs
