#include "lite_wallet_lifecycle_ui_adapter.h"

#include <sstream>
#include <utility>

namespace dragonx {
namespace wallet {

namespace {

void addIssue(std::vector<LiteWalletLifecycleUiExecutionIssueInfo>& issues,
              LiteWalletLifecycleUiExecutionIssue issue,
              std::string message)
{
    issues.push_back(LiteWalletLifecycleUiExecutionIssueInfo{issue, std::move(message)});
}

LiteWalletLifecycleUiExecutionResult stoppedResult(
    LiteWalletLifecycleUiExecutionResult result,
    LiteWalletLifecycleUiExecutionStatus status,
    LiteWalletLifecycleUiExecutionIssue issue,
    const std::string& message)
{
    result.status = status;
    result.error = message;
    result.ok = false;
    addIssue(result.issues, issue, message);
    return result;
}

LiteWalletLifecycleUiExecutionStatus statusFromServerSelection(
    LiteWalletServerSelectionUiExecutionStatus status)
{
    switch (status) {
        case LiteWalletServerSelectionUiExecutionStatus::ReadyForFutureLifecycle:
            return LiteWalletLifecycleUiExecutionStatus::ReadyForFutureLifecycleRequest;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForLiteBuild:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForLiteBuild;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForBackendCapability:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForBackendCapability;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForSettings:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForSettings;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForServerSelection:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForServerSelection;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForPersistenceOwner:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForPersistedServerSelection;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForDisplayStatus:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForDisplayStatus;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForLifecycleUi:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForLifecycleUi;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForPrivateDataRedaction:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForPrivateDataRedaction;
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForSyncPlannerFeed:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForSyncPlannerFeed;
        case LiteWalletServerSelectionUiExecutionStatus::SettingsSaveFailed:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForSettings;
        case LiteWalletServerSelectionUiExecutionStatus::RuntimeExecutionDisabled:
            return LiteWalletLifecycleUiExecutionStatus::RuntimeExecutionDisabled;
    }
    return LiteWalletLifecycleUiExecutionStatus::WaitingForSettings;
}

LiteWalletLifecycleUiExecutionStatus statusFromLifecycle(
    LiteWalletServerLifecycleReadinessStatus status)
{
    switch (status) {
        case LiteWalletServerLifecycleReadinessStatus::ReadyForFutureLifecycle:
            return LiteWalletLifecycleUiExecutionStatus::ReadyForFutureLifecycleRequest;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForLiteBuild:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForLiteBuild;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForBackendCapability:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForBackendCapability;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForServerSelection:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForServerSelection;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForPersistenceIntent:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForPersistedServerSelection;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForDisplayStatus:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForDisplayStatus;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForLifecycleUi:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForLifecycleUi;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForPrivateDataRedaction:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForPrivateDataRedaction;
        case LiteWalletServerLifecycleReadinessStatus::WaitingForSyncPlannerFeed:
            return LiteWalletLifecycleUiExecutionStatus::WaitingForSyncPlannerFeed;
        case LiteWalletServerLifecycleReadinessStatus::RuntimeExecutionDisabled:
            return LiteWalletLifecycleUiExecutionStatus::RuntimeExecutionDisabled;
    }
    return LiteWalletLifecycleUiExecutionStatus::WaitingForSettings;
}

LiteWalletLifecycleUiExecutionIssue issueFromLifecycle(
    LiteWalletServerLifecycleReadinessIssue issue)
{
    switch (issue) {
        case LiteWalletServerLifecycleReadinessIssue::FullNodeBuild:
            return LiteWalletLifecycleUiExecutionIssue::FullNodeBuild;
        case LiteWalletServerLifecycleReadinessIssue::LiteBackendCapabilityMissing:
            return LiteWalletLifecycleUiExecutionIssue::LiteBackendCapabilityMissing;
        case LiteWalletServerLifecycleReadinessIssue::PersistedSettingsNotLoaded:
            return LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded;
        case LiteWalletServerLifecycleReadinessIssue::PersistedServerSelectionIntentMissing:
            return LiteWalletLifecycleUiExecutionIssue::PersistedServerSelectionIntentMissing;
        case LiteWalletServerLifecycleReadinessIssue::ServerSelectionMissing:
            return LiteWalletLifecycleUiExecutionIssue::ServerSelectionMissing;
        case LiteWalletServerLifecycleReadinessIssue::ServerPersistenceOwnerMissing:
            return LiteWalletLifecycleUiExecutionIssue::PersistedServerSelectionIntentMissing;
        case LiteWalletServerLifecycleReadinessIssue::SelectedServerDisplayMissing:
            return LiteWalletLifecycleUiExecutionIssue::SelectedServerDisplayMissing;
        case LiteWalletServerLifecycleReadinessIssue::LifecycleUiOwnerMissing:
            return LiteWalletLifecycleUiExecutionIssue::LifecycleUiOwnerMissing;
        case LiteWalletServerLifecycleReadinessIssue::LifecycleOperationUnconfirmed:
            return LiteWalletLifecycleUiExecutionIssue::LifecycleOperationUnconfirmed;
        case LiteWalletServerLifecycleReadinessIssue::OpenWalletPathMissing:
            return LiteWalletLifecycleUiExecutionIssue::OpenWalletPathMissing;
        case LiteWalletServerLifecycleReadinessIssue::RestoreSeedMissing:
            return LiteWalletLifecycleUiExecutionIssue::RestoreSeedMissing;
        case LiteWalletServerLifecycleReadinessIssue::PrivateDataRedactionMissing:
            return LiteWalletLifecycleUiExecutionIssue::PrivateDataRedactionMissing;
        case LiteWalletServerLifecycleReadinessIssue::SyncPlannerFeedMissing:
            return LiteWalletLifecycleUiExecutionIssue::SyncPlannerFeedMissing;
        case LiteWalletServerLifecycleReadinessIssue::RealLifecycleExecutionRequested:
            return LiteWalletLifecycleUiExecutionIssue::WalletLifecycleExecutionRequested;
    }
    return LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded;
}

LiteWalletLifecycleUiExecutionIssue issueFromServerSelection(
    LiteWalletServerSelectionUiExecutionIssue issue)
{
    switch (issue) {
        case LiteWalletServerSelectionUiExecutionIssue::FullNodeBuild:
            return LiteWalletLifecycleUiExecutionIssue::FullNodeBuild;
        case LiteWalletServerSelectionUiExecutionIssue::LiteBackendCapabilityMissing:
            return LiteWalletLifecycleUiExecutionIssue::LiteBackendCapabilityMissing;
        case LiteWalletServerSelectionUiExecutionIssue::SettingsNotLoaded:
            return LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded;
        case LiteWalletServerSelectionUiExecutionIssue::ServerSelectionMissing:
            return LiteWalletLifecycleUiExecutionIssue::ServerSelectionMissing;
        case LiteWalletServerSelectionUiExecutionIssue::ServerPersistenceOwnerMissing:
            return LiteWalletLifecycleUiExecutionIssue::PersistedServerSelectionIntentMissing;
        case LiteWalletServerSelectionUiExecutionIssue::SelectedServerDisplayMissing:
            return LiteWalletLifecycleUiExecutionIssue::SelectedServerDisplayMissing;
        case LiteWalletServerSelectionUiExecutionIssue::LifecycleUiOwnerMissing:
            return LiteWalletLifecycleUiExecutionIssue::LifecycleUiOwnerMissing;
        case LiteWalletServerSelectionUiExecutionIssue::PrivateDataRedactionMissing:
            return LiteWalletLifecycleUiExecutionIssue::PrivateDataRedactionMissing;
        case LiteWalletServerSelectionUiExecutionIssue::SyncPlannerFeedMissing:
            return LiteWalletLifecycleUiExecutionIssue::SyncPlannerFeedMissing;
        case LiteWalletServerSelectionUiExecutionIssue::SettingsSaveFailed:
            return LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded;
        case LiteWalletServerSelectionUiExecutionIssue::ServerConnectivityCheckRequested:
            return LiteWalletLifecycleUiExecutionIssue::ServerConnectivityCheckRequested;
        case LiteWalletServerSelectionUiExecutionIssue::WalletLifecycleExecutionRequested:
            return LiteWalletLifecycleUiExecutionIssue::WalletLifecycleExecutionRequested;
        case LiteWalletServerSelectionUiExecutionIssue::SyncRequested:
            return LiteWalletLifecycleUiExecutionIssue::SyncRequested;
        case LiteWalletServerSelectionUiExecutionIssue::SyncStatusPollingRequested:
            return LiteWalletLifecycleUiExecutionIssue::SyncStatusPollingRequested;
        case LiteWalletServerSelectionUiExecutionIssue::WorkerQueueRequested:
            return LiteWalletLifecycleUiExecutionIssue::WorkerQueueRequested;
        case LiteWalletServerSelectionUiExecutionIssue::WalletStateMutationRequested:
            return LiteWalletLifecycleUiExecutionIssue::WalletStateMutationRequested;
        case LiteWalletServerSelectionUiExecutionIssue::WalletFilePersistenceRequested:
            return LiteWalletLifecycleUiExecutionIssue::WalletFilePersistenceRequested;
        case LiteWalletServerSelectionUiExecutionIssue::SendImportExportRequested:
            return LiteWalletLifecycleUiExecutionIssue::SendImportExportRequested;
    }
    return LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded;
}

void copyServerSelectionIssues(LiteWalletLifecycleUiExecutionResult& result)
{
    for (const auto& issue : result.serverSelectionReport.issues) {
        addIssue(result.issues, issueFromServerSelection(issue.issue), issue.message);
    }
}

void copyLifecycleIssues(LiteWalletLifecycleUiExecutionResult& result)
{
    for (const auto& issue : result.lifecycleReadiness.issues) {
        addIssue(result.issues, issueFromLifecycle(issue.issue), issue.message);
    }
}

LiteWalletServerSelectionUiExecutionInput makeServerSelectionInput(
    const LiteWalletLifecycleUiExecutionInput& input,
    const config::Settings& settings)
{
    LiteWalletServerSelectionUiExecutionInput selection;
    selection.capabilities = input.capabilities;
    selection.persistence.settingsLoaded = input.settingsLoaded;
    selection.persistence.havePersistedSelectionIntent =
        !input.requirePersistedServerSelectionIntent || settings.getLitePersistSelectedServer();
    selection.persistence.persistSelectedServer = false;
    selection.persistence.persistenceOwnerReady = true;
    selection.persistence.writeSettings = false;
    selection.ui = input.ui;
    selection.operation = input.request.operation;
    selection.createRequest = input.request.createRequest;
    selection.openRequest = input.request.openRequest;
    selection.restoreRequest = input.request.restoreRequest;
    selection.requireLifecycleReadiness = true;
    return selection;
}

std::string buildDiagnosticSummary(const LiteWalletLifecycleUiExecutionResult& result)
{
    std::ostringstream out;
    out << "operation=" << liteWalletLifecycleOperationName(result.lifecycleInput.operation)
        << ";server=" << result.selectedServerUrlRedacted
        << ";request=" << result.requestSummaryRedacted
        << ";network=false;bridge=false;lifecycle=false;sync=false;wallet_state=false;wallet_file=false";
    return out.str();
}

} // namespace

const char* liteWalletLifecycleUiExecutionStatusName(
    LiteWalletLifecycleUiExecutionStatus status)
{
    switch (status) {
        case LiteWalletLifecycleUiExecutionStatus::ReadyForFutureLifecycleRequest:
            return "ReadyForFutureLifecycleRequest";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForLiteBuild:
            return "WaitingForLiteBuild";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForBackendCapability:
            return "WaitingForBackendCapability";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForSettings:
            return "WaitingForSettings";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForPersistedServerSelection:
            return "WaitingForPersistedServerSelection";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForServerSelection:
            return "WaitingForServerSelection";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForDisplayStatus:
            return "WaitingForDisplayStatus";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForLifecycleUi:
            return "WaitingForLifecycleUi";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForRequest:
            return "WaitingForRequest";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForPrivateDataRedaction:
            return "WaitingForPrivateDataRedaction";
        case LiteWalletLifecycleUiExecutionStatus::WaitingForSyncPlannerFeed:
            return "WaitingForSyncPlannerFeed";
        case LiteWalletLifecycleUiExecutionStatus::RuntimeExecutionDisabled:
            return "RuntimeExecutionDisabled";
    }
    return "WaitingForSettings";
}

const char* liteWalletLifecycleUiExecutionIssueName(
    LiteWalletLifecycleUiExecutionIssue issue)
{
    switch (issue) {
        case LiteWalletLifecycleUiExecutionIssue::FullNodeBuild:
            return "FullNodeBuild";
        case LiteWalletLifecycleUiExecutionIssue::LiteBackendCapabilityMissing:
            return "LiteBackendCapabilityMissing";
        case LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded:
            return "SettingsNotLoaded";
        case LiteWalletLifecycleUiExecutionIssue::PersistedServerSelectionIntentMissing:
            return "PersistedServerSelectionIntentMissing";
        case LiteWalletLifecycleUiExecutionIssue::ServerSelectionMissing:
            return "ServerSelectionMissing";
        case LiteWalletLifecycleUiExecutionIssue::SelectedServerDisplayMissing:
            return "SelectedServerDisplayMissing";
        case LiteWalletLifecycleUiExecutionIssue::LifecycleUiOwnerMissing:
            return "LifecycleUiOwnerMissing";
        case LiteWalletLifecycleUiExecutionIssue::LifecycleOperationUnconfirmed:
            return "LifecycleOperationUnconfirmed";
        case LiteWalletLifecycleUiExecutionIssue::OpenWalletPathMissing:
            return "OpenWalletPathMissing";
        case LiteWalletLifecycleUiExecutionIssue::RestoreSeedMissing:
            return "RestoreSeedMissing";
        case LiteWalletLifecycleUiExecutionIssue::PrivateDataRedactionMissing:
            return "PrivateDataRedactionMissing";
        case LiteWalletLifecycleUiExecutionIssue::SyncPlannerFeedMissing:
            return "SyncPlannerFeedMissing";
        case LiteWalletLifecycleUiExecutionIssue::ServerConnectivityCheckRequested:
            return "ServerConnectivityCheckRequested";
        case LiteWalletLifecycleUiExecutionIssue::WalletExistsCheckRequested:
            return "WalletExistsCheckRequested";
        case LiteWalletLifecycleUiExecutionIssue::WalletLifecycleExecutionRequested:
            return "WalletLifecycleExecutionRequested";
        case LiteWalletLifecycleUiExecutionIssue::SyncRequested:
            return "SyncRequested";
        case LiteWalletLifecycleUiExecutionIssue::SyncStatusPollingRequested:
            return "SyncStatusPollingRequested";
        case LiteWalletLifecycleUiExecutionIssue::WorkerQueueRequested:
            return "WorkerQueueRequested";
        case LiteWalletLifecycleUiExecutionIssue::WalletStateMutationRequested:
            return "WalletStateMutationRequested";
        case LiteWalletLifecycleUiExecutionIssue::WalletFilePersistenceRequested:
            return "WalletFilePersistenceRequested";
        case LiteWalletLifecycleUiExecutionIssue::SendImportExportRequested:
            return "SendImportExportRequested";
        case LiteWalletLifecycleUiExecutionIssue::SettingsWriteRequested:
            return "SettingsWriteRequested";
    }
    return "SettingsNotLoaded";
}

LiteWalletLifecycleUiExecutionResult executeLiteWalletLifecycleUiRequest(
    config::Settings& settings,
    const LiteWalletLifecycleUiExecutionInput& input)
{
    LiteWalletLifecycleUiExecutionResult result;

    auto stopForRuntime = [&](LiteWalletLifecycleUiExecutionIssue issue,
                              const std::string& message) {
        return stoppedResult(std::move(result),
                             LiteWalletLifecycleUiExecutionStatus::RuntimeExecutionDisabled,
                             issue,
                             message);
    };

    if (input.settingsWriteRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::SettingsWriteRequested,
                              "lite lifecycle request adapter cannot write settings");
    }
    if (input.serverConnectivityCheckRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::ServerConnectivityCheckRequested,
                              "lite lifecycle request adapter cannot check server connectivity");
    }
    if (input.walletExistsCheckRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::WalletExistsCheckRequested,
                              "lite lifecycle request adapter cannot check wallet existence");
    }
    if (input.walletLifecycleExecutionRequested || input.ui.realLifecycleExecutionRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::WalletLifecycleExecutionRequested,
                              "lite lifecycle request adapter cannot execute wallet lifecycle actions");
    }
    if (input.syncStartRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::SyncRequested,
                              "lite lifecycle request adapter cannot start sync");
    }
    if (input.syncStatusPollingRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::SyncStatusPollingRequested,
                              "lite lifecycle request adapter cannot poll syncstatus");
    }
    if (input.workerQueueEnqueueRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::WorkerQueueRequested,
                              "lite lifecycle request adapter cannot enqueue wallet workers");
    }
    if (input.walletStateMutationRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::WalletStateMutationRequested,
                              "lite lifecycle request adapter cannot mutate WalletState");
    }
    if (input.walletFilePersistenceRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::WalletFilePersistenceRequested,
                              "lite lifecycle request adapter cannot persist wallet files");
    }
    if (input.sendImportExportExecutionRequested) {
        return stopForRuntime(LiteWalletLifecycleUiExecutionIssue::SendImportExportRequested,
                              "lite lifecycle request adapter cannot execute send/import/export flows");
    }

    if (!input.settingsLoaded) {
        return stoppedResult(std::move(result),
                             LiteWalletLifecycleUiExecutionStatus::WaitingForSettings,
                             LiteWalletLifecycleUiExecutionIssue::SettingsNotLoaded,
                             "lite lifecycle settings are not loaded");
    }
    if (!input.request.requestProvided) {
        return stoppedResult(std::move(result),
                             LiteWalletLifecycleUiExecutionStatus::WaitingForRequest,
                             LiteWalletLifecycleUiExecutionIssue::LifecycleUiOwnerMissing,
                             "lite lifecycle request intent is missing");
    }
    if (input.requirePersistedServerSelectionIntent && !settings.getLitePersistSelectedServer()) {
        return stoppedResult(std::move(result),
                             LiteWalletLifecycleUiExecutionStatus::WaitingForPersistedServerSelection,
                             LiteWalletLifecycleUiExecutionIssue::PersistedServerSelectionIntentMissing,
                             "lite lifecycle requires persisted selected-server intent");
    }

    result.connectionSettings = liteConnectionSettingsFromAppSettings(settings);
    result.selectedServerSettingsLoaded = true;

    LiteWalletServerSelectionUiExecutionInput selectionInput = makeServerSelectionInput(input, settings);
    result.serverSelectionReport = executeLiteWalletServerSelectionUi(settings, selectionInput);
    result.connectionSettings = result.serverSelectionReport.connectionSettings;
    result.selectedServer = result.serverSelectionReport.selectedServer;
    result.selectedServerUrlRedacted = result.serverSelectionReport.selectedServerUrlRedacted;
    result.selectedServerIntentAccepted = result.serverSelectionReport.selectedServerIntentAccepted;
    result.status = statusFromServerSelection(result.serverSelectionReport.status);

    result.lifecycleInput = result.serverSelectionReport.lifecycleInput;
    result.lifecycleReadiness = result.serverSelectionReport.lifecycleReadiness;
    result.lifecycleReadinessAccepted = result.lifecycleReadiness.ok;
    result.lifecycleReportCouldFeedSyncPlanners =
        result.lifecycleReadiness.lifecycleReportCouldFeedSyncPlanners;
    result.requestAccepted = result.lifecycleReadiness.requestAccepted;
    result.privateDataRedacted = result.lifecycleReadiness.requestPlan.privateInputsRedacted;
    result.privateData = result.lifecycleReadiness.requestPlan.privateData;
    result.requestSummaryRedacted = result.lifecycleReadiness.requestPlan.requestSummary;
    result.diagnosticSummary = buildDiagnosticSummary(result);
    copyServerSelectionIssues(result);
    copyLifecycleIssues(result);

    const bool lifecycleReadinessEvaluated = result.lifecycleReadiness.ok ||
        !result.lifecycleReadiness.issues.empty() ||
        !result.lifecycleReadiness.error.empty() ||
        result.lifecycleReadiness.serverSelectionAccepted ||
        result.lifecycleReadiness.requestAccepted;

    if (!result.serverSelectionReport.ok && !lifecycleReadinessEvaluated) {
        result.ok = false;
        result.status = statusFromServerSelection(result.serverSelectionReport.status);
        result.error = result.serverSelectionReport.error;
        return result;
    }

    if (!result.lifecycleReadiness.ok) {
        result.ok = false;
        result.status = statusFromLifecycle(result.lifecycleReadiness.status);
        result.error = result.lifecycleReadiness.error.empty()
            ? result.serverSelectionReport.error
            : result.lifecycleReadiness.error;
        return result;
    }

    result.ok = true;
    result.status = LiteWalletLifecycleUiExecutionStatus::ReadyForFutureLifecycleRequest;
    return result;
}

} // namespace wallet
} // namespace dragonx