/**
 * Copyright (c) 2015-2026 Governikus GmbH & Co. KG, Germany
 */

#include "StateConnectCard.h"

#include "CardConnection.h"
#include "ReaderManager.h"
#include "Survey.h"
#include "VolatileSettings.h"

#include <QLoggingCategory>

Q_DECLARE_LOGGING_CATEGORY(statemachine)

using namespace governikus;

StateConnectCard::StateConnectCard(const QSharedPointer<WorkflowContext>& pContext)
	: AbstractState(pContext)
	, GenericContextContainer(pContext)
{
}


void StateConnectCard::run()
{
	const auto* readerManager = Env::getSingleton<ReaderManager>();
	*this << connect(readerManager, &ReaderManager::fireCardInserted, this, &StateConnectCard::onCardInserted);
	*this << connect(readerManager, &ReaderManager::fireCardRemoved, this, &StateConnectCard::onUnusableCardConnectionLost);
	*this << connect(readerManager, &ReaderManager::fireReaderRemoved, this, &StateConnectCard::onUnusableCardConnectionLost);
	onCardInserted();
}


void StateConnectCard::onCardInserted()
{
	auto* readerManager = Env::getSingleton<ReaderManager>();
	ReaderInfo readerInfo = readerManager->getReaderInfo(getContext()->getReaderName());
	Env::getSingleton<Survey>()->setReaderInfo(readerInfo);

	if (readerInfo.hasEid())
	{
		qCDebug(statemachine) << "Card has been inserted, trying to connect";
		*this << readerManager->callCreateCardConnectionCommand(readerInfo.getName(), this, &StateConnectCard::onCommandDone);
	}
}


void StateConnectCard::onCommandDone(QSharedPointer<CreateCardConnectionCommand> pCommand)
{
	if (!isActive())
	{
		qCDebug(statemachine) << "State not active anymore, ignore CardConnectionCommand result.";
		return;
	}

	const auto& cardConnection = pCommand->getCardConnection();

	qCDebug(statemachine) << "Card connection command completed";
	if (cardConnection == nullptr)
	{
		qCDebug(statemachine) << "Card connection failed";
		updateStatus(GlobalStatus::Code::Card_Communication_Error);
		Q_EMIT fireAbort(FailureCode::Reason::Connect_Card_Connection_Failed);
		return;
	}

	const auto& context = getContext();
	qCDebug(statemachine) << "Card connection was successful";
	context->setCardInitiallyAppeared();
	context->setCardConnection(cardConnection);

	const auto& readerInfo = cardConnection->getReaderInfo();
	Q_ASSERT(!readerInfo.insufficientApduLength());

	if (context->eidTypeMismatch())
	{
		//: IOS
		context->getCardConnection()->setErrorMessage(tr("The used ID card type is not accepted by the server."));
		return;
	}

	if (readerInfo.isPinDeactivated() && !context->isCanAllowedMode())
	{
		qCDebug(statemachine) << "The eID function of the ID card is not activated.";
		const GlobalStatus status = GlobalStatus::Code::Card_Pin_Deactivated;
		if (Env::getSingleton<VolatileSettings>()->isUsedAsSDK())
		{
			context->getCardConnection()->setErrorMessage(status.toErrorDescription());
		}
		else
		{
			updateStatus(status);
			Q_EMIT fireAbort(FailureCode::Reason::Connect_Card_Eid_Inactive);
		}
		return;
	}

	Q_EMIT fireContinue();
}


void StateConnectCard::onUnusableCardConnectionLost(const ReaderInfo& pInfo)
{
	if (pInfo.getName() == getContext()->getReaderName())
	{
		getContext()->setReaderName(QString());
		getContext()->resetCardConnection();
		Q_EMIT fireRetry();
	}
}


void StateConnectCard::onEntry(QEvent* pEvent)
{
	AbstractState::onEntry(pEvent);

	const WorkflowContext* const context = getContext().data();
	Q_ASSERT(context);

	/*
	 * Note: the plugin types to be used in this state must be already set in the workflow context before this state is entered.
	 * Changing the plugin types in the context, e.g. from {NFC} to {REMOTE}, causes the state to be left with a fireRetry signal.
	 */
	*this << connect(context, &WorkflowContext::fireReaderPluginTypesChanged, this, &StateConnectCard::fireRetry);
}
