/*
 * 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 "src/datovka_shared/utility/date_time.h"
#include "src/delegates/account_status_value.h"
#include "src/io/isds_sessions.h"
#include "src/models/account_status_model.h"
#include "src/settings/accounts.h"

AccountStatusModel::AccountStatusModel(AccountsMap *accounts,
    IsdsSessions *sessions, QObject *parent)
    : QAbstractListModel(parent),
    m_accounts(accounts),
    m_sessions(sessions)
{
	if (Q_NULLPTR != m_accounts) {
		connect(m_accounts, SIGNAL(accountAboutToBeAdded(AcntId, int)),
		    this, SLOT(watchAccountAboutToBeAdded(AcntId, int)));
		connect(m_accounts, SIGNAL(accountAdded(AcntId, int)),
		    this, SLOT(watchAccountAdded(AcntId, int)));

		connect(m_accounts, SIGNAL(accountAboutToBeRemoved(AcntId, int)),
		    this, SLOT(watchAccountAboutToBeRemoved(AcntId, int)));
		connect(m_accounts, SIGNAL(accountRemoved(AcntId, int)),
		    this, SLOT(watchAccountRemoved(AcntId, int)));

		connect(m_accounts, SIGNAL(accountsAboutToBeMoved(int, int, int)),
		    this, SLOT(watchAccountsAboutToBeMoved(int, int, int)));
		connect(m_accounts, SIGNAL(accountsMoved(int, int, int)),
		    this, SLOT(watchAccountsMoved(int, int, int)));

		connect(m_accounts, SIGNAL(accountUsernameChanged(AcntId, AcntId, int)),
		    this, SLOT(watchUsernameChanged(AcntId, AcntId, int)));
		connect(m_accounts, SIGNAL(accountDataChanged(AcntId)),
		    this, SLOT(watchAccountDataChanged(AcntId)));

		connect(m_accounts, SIGNAL(contentAboutToBeReset()),
		    this, SLOT(watchContentAboutToBeReset()));

		connect(m_accounts, SIGNAL(contentReset()),
		    this, SLOT(watchContentReset()));

		if (Q_NULLPTR != m_sessions) {
			connect(m_sessions, SIGNAL(addedSession(QString, int)),
			    this, SLOT(watchSessionChanges(QString, int)));
			connect(m_sessions, SIGNAL(removedSession(QString, int)),
			    this, SLOT(watchSessionChanges(QString, int)));
		}
	}
}

int AccountStatusModel::rowCount(const QModelIndex &parent) const
{
	if (Q_UNLIKELY(Q_NULLPTR == m_accounts)) {
		return 0;
	}

	if (!parent.isValid()) {
		return m_accounts->acntIds().size();
	}

	return 0;
}

QVariant AccountStatusModel::data(const QModelIndex &index, int role) const
{
	if (Q_UNLIKELY((Q_NULLPTR == m_accounts))
	        || (Q_NULLPTR == m_sessions)) {
		return QVariant();
	}

	if (Q_UNLIKELY(!index.isValid())) {
		return QVariant();
	}

	const int row = index.row();
	if (Q_UNLIKELY((row < 0) || (row >= rowCount()))) {
		Q_ASSERT(0);
		return QVariant();
	}

	const AcntId &aId(m_accounts->acntIds()[row]);
	if (Q_UNLIKELY(!aId.isValid())) {
		Q_ASSERT(0);
		return QVariant();
	}

	switch (role) {
	case Qt::DisplayRole:
		{
			const AcntData &aData = m_accounts->acntData(aId);
			return QVariant::fromValue(
			    AccountStatusValue(aData.accountName(),
			        aData.userName(),
			        aData.loginMethod(),
			        m_sessions->holdsSession(aData.userName())));
		}
		break;
	case Qt::ToolTipRole:
		{
			const AcntData &aData = m_accounts->acntData(aId);
			const QDateTime creationTime =
			    m_sessions->creationTime(aData.userName());
			if (creationTime.isValid()) {
				return tr("Created at %1").arg(
				    creationTime.toLocalTime().toString(
				        Utility::dateTimeDisplayFormat));
			}
			return QVariant();
		}
		break;
	default:
		return QVariant();
		break;
	}
}

Qt::ItemFlags AccountStatusModel::flags(const QModelIndex &index) const
{
	Qt::ItemFlags defaultFlags = QAbstractListModel::flags(index);

	const int row = index.row();

	const AcntId &aId(m_accounts->acntIds()[row]);

	const AcntData &aData = m_accounts->acntData(aId);
	if (m_sessions->holdsSession(aData.userName())) {
		defaultFlags |= Qt::ItemIsEditable;
	}

	return defaultFlags;
}

bool AccountStatusModel::setData(const QModelIndex &index,
    const QVariant &value, int role)
{
	if (Q_UNLIKELY(!index.isValid())) {
		return false;
	}

	int row = index.row();
	if (Q_UNLIKELY((row < 0) || (row >= rowCount()))) {
		return false;
	}

	if (Q_UNLIKELY(!value.canConvert<AccountStatusValue>())) {
		return false;
	}

	if (Q_UNLIKELY(Qt::EditRole != role)) {
		return false;
	}

	const AccountStatusValue currentStatusValue =
	    qvariant_cast<AccountStatusValue>(index.data());
	const AccountStatusValue newStatusValue =
	    qvariant_cast<AccountStatusValue>(value);

	if (currentStatusValue == newStatusValue) {
		return false;
	}

	if (!newStatusValue.sessionActive) {
		m_sessions->quitSession(newStatusValue.username);
	}

	emit dataChanged(index, index);
	return true;
}

void AccountStatusModel::watchAccountAboutToBeAdded(const AcntId &acntId, int row)
{
	Q_UNUSED(acntId);

	beginInsertRows(QModelIndex(), row, row);
}

void AccountStatusModel::watchAccountAdded(const AcntId &acntId, int row)
{
	Q_UNUSED(acntId);
	Q_UNUSED(row);

	endInsertRows();
}

void AccountStatusModel::watchAccountAboutToBeRemoved(const AcntId &acntId, int row)
{
	Q_UNUSED(acntId);

	beginRemoveRows(QModelIndex(), row, row);
}

void AccountStatusModel::watchAccountRemoved(const AcntId &acntId, int row)
{
	Q_UNUSED(acntId);
	Q_UNUSED(row);

	endRemoveRows();
}

void AccountStatusModel::watchAccountsAboutToBeMoved(int srcFrst, int srcLst, int dst)
{
	const QModelIndex parent;

	beginMoveRows(parent, srcFrst, srcLst, parent, dst);
}

void AccountStatusModel::watchAccountsMoved(int srcFrst, int srcLst, int dst)
{
	Q_UNUSED(srcFrst);
	Q_UNUSED(srcLst);
	Q_UNUSED(dst);

	endMoveRows();
}

void AccountStatusModel::watchUsernameChanged(const AcntId &oldId,
    const AcntId &newId, int index)
{
	Q_UNUSED(oldId);
	Q_UNUSED(index);

	int row = m_accounts->acntIds().indexOf(newId);
	if (row >= 0) {
		QModelIndex idx = AccountStatusModel::index(row, 0);
		emit dataChanged(idx, idx);
	}
}

void AccountStatusModel::watchAccountDataChanged(const AcntId &acntId)
{
	int row = m_accounts->acntIds().indexOf(acntId);
	if (row >= 0) {
		QModelIndex idx = AccountStatusModel::index(row, 0);
		emit dataChanged(idx, idx);
	}
}

void AccountStatusModel::watchContentAboutToBeReset(void)
{
	beginResetModel();
}

void AccountStatusModel::watchContentReset(void)
{
	endResetModel();
}

void AccountStatusModel::watchSessionChanges(const QString &username, int nowActive)
{
	Q_UNUSED(nowActive);

	const AcntId aId = m_accounts->acntIdFromUsername(username);

	int row = m_accounts->acntIds().indexOf(aId);
	if (row >= 0) {
		QModelIndex idx = AccountStatusModel::index(row, 0);
		emit dataChanged(idx, idx);
	}
}
