#include "lite_wallet_server_selection_adapter.h"

#include <algorithm>
#include <cctype>
#include <sstream>
#include <utility>

namespace dragonx {
namespace wallet {

namespace {

std::string trimCopy(const std::string& value)
{
    const auto first = std::find_if_not(value.begin(), value.end(), [](unsigned char ch) {
        return std::isspace(ch) != 0;
    });
    const auto last = std::find_if_not(value.rbegin(), value.rend(), [](unsigned char ch) {
        return std::isspace(ch) != 0;
    }).base();
    if (first >= last) return {};
    return std::string(first, last);
}

LiteServerSelectionMode walletModeFromSettings(
    config::Settings::LiteServerSelectionPreferenceMode mode)
{
    switch (mode) {
        case config::Settings::LiteServerSelectionPreferenceMode::Sticky:
            return LiteServerSelectionMode::Sticky;
        case config::Settings::LiteServerSelectionPreferenceMode::Random:
            return LiteServerSelectionMode::Random;
    }
    return LiteServerSelectionMode::Sticky;
}

config::Settings::LiteServerSelectionPreferenceMode settingsModeFromWallet(
    LiteServerSelectionMode mode)
{
    switch (mode) {
        case LiteServerSelectionMode::Sticky:
            return config::Settings::LiteServerSelectionPreferenceMode::Sticky;
        case LiteServerSelectionMode::Random:
            return config::Settings::LiteServerSelectionPreferenceMode::Random;
    }
    return config::Settings::LiteServerSelectionPreferenceMode::Sticky;
}

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

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

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

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

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

std::string buildDiagnosticSummary(const LiteWalletServerSelectionUiExecutionResult& result)
{
    std::ostringstream out;
    out << "mode=" << liteServerSelectionModeName(result.connectionSettings.selectionMode)
        << ";server=" << result.selectedServerUrlRedacted
        << ";settings_written=" << (result.settingsWritten ? "true" : "false")
        << ";network=false;bridge=false;lifecycle=false;sync=false;wallet_state=false";
    return out.str();
}

LiteConnectionSettings settingsWithIntent(const LiteConnectionSettings& current,
                                          const LiteWalletServerSelectionUiIntent& intent)
{
    LiteConnectionSettings settings = current;
    if (!intent.selectedServerIntentProvided) return settings;

    settings.selectionMode = intent.selectionMode;
    if (!intent.chainName.empty()) settings.chainName = trimCopy(intent.chainName);
    if (intent.replaceServers) settings.servers = intent.servers;

    switch (intent.selectionMode) {
        case LiteServerSelectionMode::Sticky:
            settings.stickyServerUrl = trimCopy(intent.selectedServerUrl);
            break;
        case LiteServerSelectionMode::Random:
            settings.randomSelectionSeed = intent.randomSelectionSeed;
            break;
    }
    return settings;
}

LiteWalletServerLifecycleReadinessInput makeLifecycleInput(
    const LiteWalletServerSelectionUiExecutionInput& input,
    const LiteConnectionSettings& connectionSettings)
{
    LiteWalletServerLifecycleReadinessInput lifecycle;
    lifecycle.capabilities = input.capabilities;
    lifecycle.settings = connectionSettings;
    lifecycle.operation = input.operation;
    lifecycle.persistence.settingsLoaded = input.persistence.settingsLoaded;
    lifecycle.persistence.havePersistedSelectionIntent =
        input.persistence.havePersistedSelectionIntent || input.intent.selectedServerIntentProvided;
    lifecycle.persistence.persistSelectedServer = input.persistence.persistSelectedServer;
    lifecycle.persistence.persistenceOwnerReady = input.persistence.persistenceOwnerReady;
    lifecycle.ui = input.ui;
    lifecycle.ui.realLifecycleExecutionRequested =
        lifecycle.ui.realLifecycleExecutionRequested || input.walletLifecycleExecutionRequested;
    lifecycle.createRequest = input.createRequest;
    lifecycle.openRequest = input.openRequest;
    lifecycle.restoreRequest = input.restoreRequest;
    return lifecycle;
}

} // namespace

LiteConnectionSettings liteConnectionSettingsFromAppSettings(const config::Settings& settings)
{
    LiteConnectionSettings connectionSettings;
    connectionSettings.servers.clear();
    for (const auto& server : settings.getLiteServers()) {
        connectionSettings.servers.push_back(LiteServerEndpoint{
            trimCopy(server.url),
            server.label,
            server.enabled
        });
    }
    connectionSettings.selectionMode = walletModeFromSettings(settings.getLiteServerSelectionMode());
    connectionSettings.stickyServerUrl = trimCopy(settings.getLiteStickyServerUrl());
    connectionSettings.chainName = trimCopy(settings.getLiteChainName());
    if (connectionSettings.chainName.empty()) connectionSettings.chainName = kDragonXLiteChainName;
    connectionSettings.randomSelectionSeed = settings.getLiteRandomSelectionSeed();
    return connectionSettings;
}

void applyLiteConnectionSettingsToAppSettings(config::Settings& settings,
                                              const LiteConnectionSettings& connectionSettings)
{
    std::vector<config::Settings::LiteServerPreference> servers;
    servers.reserve(connectionSettings.servers.size());
    for (const auto& server : connectionSettings.servers) {
        servers.push_back(config::Settings::LiteServerPreference{
            trimCopy(server.url),
            server.label,
            server.enabled
        });
    }

    settings.setLiteServerSelectionMode(settingsModeFromWallet(connectionSettings.selectionMode));
    settings.setLiteStickyServerUrl(trimCopy(connectionSettings.stickyServerUrl));
    settings.setLiteChainName(trimCopy(connectionSettings.chainName).empty()
        ? kDragonXLiteChainName
        : trimCopy(connectionSettings.chainName));
    settings.setLiteRandomSelectionSeed(connectionSettings.randomSelectionSeed);
    settings.setLiteServers(servers);
}

const char* liteWalletServerSelectionUiExecutionStatusName(
    LiteWalletServerSelectionUiExecutionStatus status)
{
    switch (status) {
        case LiteWalletServerSelectionUiExecutionStatus::ReadyForFutureLifecycle:
            return "ReadyForFutureLifecycle";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForLiteBuild:
            return "WaitingForLiteBuild";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForBackendCapability:
            return "WaitingForBackendCapability";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForSettings:
            return "WaitingForSettings";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForServerSelection:
            return "WaitingForServerSelection";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForPersistenceOwner:
            return "WaitingForPersistenceOwner";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForDisplayStatus:
            return "WaitingForDisplayStatus";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForLifecycleUi:
            return "WaitingForLifecycleUi";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForPrivateDataRedaction:
            return "WaitingForPrivateDataRedaction";
        case LiteWalletServerSelectionUiExecutionStatus::WaitingForSyncPlannerFeed:
            return "WaitingForSyncPlannerFeed";
        case LiteWalletServerSelectionUiExecutionStatus::SettingsSaveFailed:
            return "SettingsSaveFailed";
        case LiteWalletServerSelectionUiExecutionStatus::RuntimeExecutionDisabled:
            return "RuntimeExecutionDisabled";
    }
    return "WaitingForSettings";
}

const char* liteWalletServerSelectionUiExecutionIssueName(
    LiteWalletServerSelectionUiExecutionIssue issue)
{
    switch (issue) {
        case LiteWalletServerSelectionUiExecutionIssue::FullNodeBuild:
            return "FullNodeBuild";
        case LiteWalletServerSelectionUiExecutionIssue::LiteBackendCapabilityMissing:
            return "LiteBackendCapabilityMissing";
        case LiteWalletServerSelectionUiExecutionIssue::SettingsNotLoaded:
            return "SettingsNotLoaded";
        case LiteWalletServerSelectionUiExecutionIssue::ServerSelectionMissing:
            return "ServerSelectionMissing";
        case LiteWalletServerSelectionUiExecutionIssue::ServerPersistenceOwnerMissing:
            return "ServerPersistenceOwnerMissing";
        case LiteWalletServerSelectionUiExecutionIssue::SelectedServerDisplayMissing:
            return "SelectedServerDisplayMissing";
        case LiteWalletServerSelectionUiExecutionIssue::LifecycleUiOwnerMissing:
            return "LifecycleUiOwnerMissing";
        case LiteWalletServerSelectionUiExecutionIssue::PrivateDataRedactionMissing:
            return "PrivateDataRedactionMissing";
        case LiteWalletServerSelectionUiExecutionIssue::SyncPlannerFeedMissing:
            return "SyncPlannerFeedMissing";
        case LiteWalletServerSelectionUiExecutionIssue::SettingsSaveFailed:
            return "SettingsSaveFailed";
        case LiteWalletServerSelectionUiExecutionIssue::ServerConnectivityCheckRequested:
            return "ServerConnectivityCheckRequested";
        case LiteWalletServerSelectionUiExecutionIssue::WalletLifecycleExecutionRequested:
            return "WalletLifecycleExecutionRequested";
        case LiteWalletServerSelectionUiExecutionIssue::SyncRequested:
            return "SyncRequested";
        case LiteWalletServerSelectionUiExecutionIssue::SyncStatusPollingRequested:
            return "SyncStatusPollingRequested";
        case LiteWalletServerSelectionUiExecutionIssue::WorkerQueueRequested:
            return "WorkerQueueRequested";
        case LiteWalletServerSelectionUiExecutionIssue::WalletStateMutationRequested:
            return "WalletStateMutationRequested";
        case LiteWalletServerSelectionUiExecutionIssue::WalletFilePersistenceRequested:
            return "WalletFilePersistenceRequested";
        case LiteWalletServerSelectionUiExecutionIssue::SendImportExportRequested:
            return "SendImportExportRequested";
    }
    return "SettingsNotLoaded";
}

std::string redactLiteServerSelectionValue(const std::string& value)
{
    const std::string trimmed = trimCopy(value);
    if (trimmed.empty()) return "<empty>";
    const auto scheme = trimmed.find("://");
    if (scheme == std::string::npos) return "<redacted>";
    return trimmed.substr(0, scheme + 3) + "<redacted>";
}

LiteWalletServerSelectionUiExecutionResult executeLiteWalletServerSelectionUi(
    config::Settings& settings,
    const LiteWalletServerSelectionUiExecutionInput& input)
{
    LiteWalletServerSelectionUiExecutionResult result;

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

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

    if (!input.persistence.settingsLoaded) {
        return stoppedResult(std::move(result),
                             LiteWalletServerSelectionUiExecutionStatus::WaitingForSettings,
                             LiteWalletServerSelectionUiExecutionIssue::SettingsNotLoaded,
                             "lite server settings are not loaded");
    }

    result.connectionSettings = settingsWithIntent(
        liteConnectionSettingsFromAppSettings(settings), input.intent);
    if (input.intent.selectedServerIntentProvided &&
        input.intent.selectionMode == LiteServerSelectionMode::Sticky &&
        !isLiteServerUrlUsable(trimCopy(input.intent.selectedServerUrl))) {
        result.selectedServerUrlRedacted = redactLiteServerSelectionValue(input.intent.selectedServerUrl);
        result.selectedServerRedacted = true;
        return stoppedResult(std::move(result),
                             LiteWalletServerSelectionUiExecutionStatus::WaitingForServerSelection,
                             LiteWalletServerSelectionUiExecutionIssue::ServerSelectionMissing,
                             "lite server selection URL is not usable");
    }
    result.selectedServer = selectLiteServer(result.connectionSettings);
    result.selectedServerUrlRedacted = result.selectedServer.ok
        ? redactLiteServerSelectionValue(result.selectedServer.server.url)
        : redactLiteServerSelectionValue(result.connectionSettings.stickyServerUrl);
    result.selectedServerRedacted = true;

    if (!result.selectedServer.ok) {
        return stoppedResult(std::move(result),
                             LiteWalletServerSelectionUiExecutionStatus::WaitingForServerSelection,
                             LiteWalletServerSelectionUiExecutionIssue::ServerSelectionMissing,
                             result.selectedServer.error.empty()
                                 ? "lite server selection is missing"
                                 : result.selectedServer.error);
    }
    result.selectedServerIntentAccepted = true;

    if ((input.persistence.persistSelectedServer || input.persistence.writeSettings) &&
        !input.persistence.persistenceOwnerReady) {
        return stoppedResult(std::move(result),
                             LiteWalletServerSelectionUiExecutionStatus::WaitingForPersistenceOwner,
                             LiteWalletServerSelectionUiExecutionIssue::ServerPersistenceOwnerMissing,
                             "lite server settings persistence owner is not ready");
    }
    result.settingsPersistenceAccepted = true;

    if (input.persistence.persistSelectedServer || input.persistence.writeSettings) {
        applyLiteConnectionSettingsToAppSettings(settings, result.connectionSettings);
        settings.setLitePersistSelectedServer(input.persistence.persistSelectedServer);
        result.settingsMutated = true;
    }

    if (input.persistence.writeSettings) {
        result.noSettingsPersistence = false;
        const bool saved = input.persistence.settingsSavePath.empty()
            ? settings.save()
            : settings.save(input.persistence.settingsSavePath);
        if (!saved) {
            return stoppedResult(std::move(result),
                                 LiteWalletServerSelectionUiExecutionStatus::SettingsSaveFailed,
                                 LiteWalletServerSelectionUiExecutionIssue::SettingsSaveFailed,
                                 "failed to save lite server settings");
        }
        result.settingsWritten = true;
    }

    result.lifecycleInput = makeLifecycleInput(input, result.connectionSettings);
    result.lifecycleReadiness = evaluateLiteWalletServerLifecycleReadiness(result.lifecycleInput);
    result.lifecycleReadinessAccepted = result.lifecycleReadiness.ok;
    result.lifecycleReportCouldFeedSyncPlanners = result.lifecycleReadiness.lifecycleReportCouldFeedSyncPlanners;
    result.status = statusFromLifecycle(result.lifecycleReadiness.status);
    copyLifecycleIssues(result);
    result.diagnosticSummary = buildDiagnosticSummary(result);

    if (input.requireLifecycleReadiness && !result.lifecycleReadiness.ok) {
        result.ok = false;
        result.error = result.lifecycleReadiness.error;
        return result;
    }

    result.ok = true;
    return result;
}

} // namespace wallet
} // namespace dragonx