// DragonX Wallet - ImGui Edition
// Copyright 2024-2026 The Hush Developers
// Released under the GPLv3

#include "lite_wallet_lifecycle_service.h"

#include <utility>

namespace dragonx {
namespace wallet {

namespace {

LiteRedactedPrivateData redactedField(LitePrivateDataKind kind, const std::string& value)
{
    return LiteRedactedPrivateData{kind, !value.empty(), redactLitePrivateDataValue(value)};
}

std::vector<LiteRedactedPrivateData> createPrivateData(const LiteWalletCreateRequest& request)
{
    return {redactedField(LitePrivateDataKind::Passphrase, request.passphrase)};
}

std::vector<LiteRedactedPrivateData> openPrivateData(const LiteWalletOpenRequest& request)
{
    return {
        redactedField(LitePrivateDataKind::WalletPath, request.walletPath),
        redactedField(LitePrivateDataKind::Passphrase, request.passphrase)
    };
}

std::vector<LiteRedactedPrivateData> restorePrivateData(const LiteWalletRestoreRequest& request)
{
    return {
        redactedField(LitePrivateDataKind::SeedPhrase, request.seedPhrase),
        redactedField(LitePrivateDataKind::WalletPath, request.walletPath),
        redactedField(LitePrivateDataKind::Passphrase, request.passphrase)
    };
}

WalletBackendStatus lifecycleCompletedStatus()
{
    return WalletBackendStatus{
        WalletBackendState::Disconnected,
        "lite wallet lifecycle bridge call completed; sync is not implemented",
        {},
        {},
        0.0
    };
}

} // namespace

const char* liteWalletLifecycleOperationName(LiteWalletLifecycleOperation operation)
{
    switch (operation) {
    case LiteWalletLifecycleOperation::CreateNew:
        return "CreateNew";
    case LiteWalletLifecycleOperation::OpenExisting:
        return "OpenExisting";
    case LiteWalletLifecycleOperation::RestoreFromSeed:
        return "RestoreFromSeed";
    }
    return "Unknown";
}

const char* liteWalletLifecycleAvailabilityName(LiteWalletLifecycleAvailability availability)
{
    switch (availability) {
    case LiteWalletLifecycleAvailability::Ready:
        return "Ready";
    case LiteWalletLifecycleAvailability::UnsupportedBuild:
        return "UnsupportedBuild";
    case LiteWalletLifecycleAvailability::BackendUnavailable:
        return "BackendUnavailable";
    case LiteWalletLifecycleAvailability::BridgeUnavailable:
        return "BridgeUnavailable";
    case LiteWalletLifecycleAvailability::BridgeCallsDisabled:
        return "BridgeCallsDisabled";
    case LiteWalletLifecycleAvailability::NoUsableServer:
        return "NoUsableServer";
    case LiteWalletLifecycleAvailability::RolloutDisabled:
        return "RolloutDisabled";
    }
    return "Unknown";
}

const char* litePrivateDataKindName(LitePrivateDataKind kind)
{
    switch (kind) {
    case LitePrivateDataKind::SeedPhrase:
        return "SeedPhrase";
    case LitePrivateDataKind::Passphrase:
        return "Passphrase";
    case LitePrivateDataKind::WalletPath:
        return "WalletPath";
    case LitePrivateDataKind::BridgeResponse:
        return "BridgeResponse";
    }
    return "Unknown";
}

std::string redactLitePrivateDataValue(const std::string& value)
{
    return value.empty() ? "<empty>" : "<redacted>";
}

LiteWalletLifecycleService::LiteWalletLifecycleService(WalletCapabilities capabilities,
                                                       LiteConnectionSettings connectionSettings,
                                                       LiteClientBridge* bridge,
                                                       LiteWalletLifecycleOptions options)
    : capabilities_(capabilities),
      connectionSettings_(std::move(connectionSettings)),
      bridge_(bridge),
      options_(options)
{
}

LiteWalletLifecycleAvailability LiteWalletLifecycleService::availability() const
{
    if (!isLiteBuild(capabilities_)) return LiteWalletLifecycleAvailability::UnsupportedBuild;
    if (!supportsLiteBackend(capabilities_)) return LiteWalletLifecycleAvailability::BackendUnavailable;
    if (!bridge_ || !bridge_->available()) return LiteWalletLifecycleAvailability::BridgeUnavailable;
    // Runtime kill-switch / staged-rollout gate: a structural readiness check, applied before
    // server selection so a gated-off wallet reports the rollout reason rather than a server error.
    if (options_.rolloutBlocked) return LiteWalletLifecycleAvailability::RolloutDisabled;
    if (!selectLiteServer(connectionSettings_).ok) return LiteWalletLifecycleAvailability::NoUsableServer;
    if (!options_.allowBridgeCalls) return LiteWalletLifecycleAvailability::BridgeCallsDisabled;
    return LiteWalletLifecycleAvailability::Ready;
}

WalletBackendStatus LiteWalletLifecycleService::status() const
{
    const auto currentAvailability = availability();
    if (currentAvailability == LiteWalletLifecycleAvailability::NoUsableServer) {
        return statusFor(currentAvailability, selectLiteServer(connectionSettings_).error);
    }
    if (currentAvailability == LiteWalletLifecycleAvailability::RolloutDisabled) {
        return statusFor(currentAvailability, options_.rolloutMessage);
    }
    return statusFor(currentAvailability);
}

LiteWalletLifecyclePlan LiteWalletLifecycleService::planCreateWallet(
    const LiteWalletCreateRequest& request) const
{
    return makePlan(LiteWalletLifecycleOperation::CreateNew,
                    request.serverUrl,
                    createPrivateData(request));
}

LiteWalletLifecyclePlan LiteWalletLifecycleService::planOpenWallet(
    const LiteWalletOpenRequest& request) const
{
    return makePlan(LiteWalletLifecycleOperation::OpenExisting,
                    request.serverUrl,
                    openPrivateData(request));
}

LiteWalletLifecyclePlan LiteWalletLifecycleService::planRestoreWallet(
    const LiteWalletRestoreRequest& request) const
{
    const std::string validationError = liteTrimCopy(request.seedPhrase).empty()
        ? "restore seed phrase is required"
        : std::string();
    return makePlan(LiteWalletLifecycleOperation::RestoreFromSeed,
                    request.serverUrl,
                    restorePrivateData(request),
                    validationError);
}

LiteWalletLifecycleResult LiteWalletLifecycleService::createWallet(
    const LiteWalletCreateRequest& request)
{
    const auto plan = planCreateWallet(request);
    if (!plan.ok) return blockedResult(plan, WalletBackendStatus{WalletBackendState::Error, plan.error, {}, {}, 0.0});

    const auto currentStatus = status();
    if (availability() != LiteWalletLifecycleAvailability::Ready) return blockedResult(plan, currentStatus);

    return executeCreate(request, plan);
}

LiteWalletLifecycleResult LiteWalletLifecycleService::openWallet(
    const LiteWalletOpenRequest& request)
{
    const auto plan = planOpenWallet(request);
    if (!plan.ok) return blockedResult(plan, WalletBackendStatus{WalletBackendState::Error, plan.error, {}, {}, 0.0});

    const auto currentStatus = status();
    if (availability() != LiteWalletLifecycleAvailability::Ready) return blockedResult(plan, currentStatus);

    return executeOpen(request, plan);
}

LiteWalletLifecycleResult LiteWalletLifecycleService::restoreWallet(
    const LiteWalletRestoreRequest& request)
{
    const auto plan = planRestoreWallet(request);
    if (!plan.ok) return blockedResult(plan, WalletBackendStatus{WalletBackendState::Error, plan.error, {}, {}, 0.0});

    const auto currentStatus = status();
    if (availability() != LiteWalletLifecycleAvailability::Ready) return blockedResult(plan, currentStatus);

    return executeRestore(request, plan);
}

LiteServerSelectionResult LiteWalletLifecycleService::selectServerForRequest(
    const std::string& serverUrl) const
{
    const std::string overrideUrl = liteTrimCopy(serverUrl);
    if (!overrideUrl.empty()) {
        if (!isLiteServerUrlUsable(overrideUrl)) {
            return LiteServerSelectionResult{false, {}, 0, false, "lite lifecycle server URL is not usable"};
        }
        return LiteServerSelectionResult{
            true,
            LiteServerEndpoint{overrideUrl, "Request", true},
            0,
            true,
            {}
        };
    }
    return selectLiteServer(connectionSettings_);
}

LiteWalletLifecyclePlan LiteWalletLifecycleService::makePlan(
    LiteWalletLifecycleOperation operation,
    const std::string& serverUrl,
    std::vector<LiteRedactedPrivateData> privateData,
    const std::string& validationError) const
{
    LiteWalletLifecyclePlan plan;
    plan.operation = operation;
    plan.privateData = std::move(privateData);
    plan.bridgeExecutionAllowed = options_.allowBridgeCalls;

    if (!validationError.empty()) {
        plan.error = validationError;
        return plan;
    }

    auto selection = selectServerForRequest(serverUrl);
    if (!selection.ok) {
        plan.error = selection.error;
        return plan;
    }

    plan.ok = true;
    plan.server = selection.server;
    plan.serverIndex = selection.serverIndex;
    plan.customServer = selection.customServer;
    return plan;
}

WalletBackendStatus LiteWalletLifecycleService::statusFor(
    LiteWalletLifecycleAvailability availability,
    const std::string& detail) const
{
    switch (availability) {
    case LiteWalletLifecycleAvailability::Ready:
        return WalletBackendStatus{
            WalletBackendState::Disconnected,
            detail.empty() ? "lite wallet lifecycle scaffold ready; sync is not implemented" : detail,
            {},
            {},
            0.0
        };
    case LiteWalletLifecycleAvailability::UnsupportedBuild:
        return WalletBackendStatus{
            WalletBackendState::Unavailable,
            "lite wallet lifecycle is unsupported in full-node builds",
            {},
            {},
            0.0
        };
    case LiteWalletLifecycleAvailability::BackendUnavailable:
        return WalletBackendStatus{
            WalletBackendState::Unavailable,
            "lite backend is not linked",
            {},
            {},
            0.0
        };
    case LiteWalletLifecycleAvailability::BridgeUnavailable:
        return WalletBackendStatus{
            WalletBackendState::Unavailable,
            detail.empty() ? (bridge_ ? bridge_->unavailableReason() : "lite bridge is unavailable") : detail,
            {},
            {},
            0.0
        };
    case LiteWalletLifecycleAvailability::BridgeCallsDisabled:
        return WalletBackendStatus{
            WalletBackendState::Unavailable,
            "lite wallet lifecycle bridge calls are disabled",
            {},
            {},
            0.0
        };
    case LiteWalletLifecycleAvailability::NoUsableServer:
        return WalletBackendStatus{
            WalletBackendState::Error,
            detail.empty() ? "no usable lite servers are configured" : detail,
            {},
            {},
            0.0
        };
    case LiteWalletLifecycleAvailability::RolloutDisabled:
        return WalletBackendStatus{
            WalletBackendState::Unavailable,
            detail.empty() ? "the lite wallet is disabled by the rollout policy" : detail,
            {},
            {},
            0.0
        };
    }

    return WalletBackendStatus{WalletBackendState::Unavailable, "unknown lite wallet lifecycle state", {}, {}, 0.0};
}

LiteWalletLifecycleResult LiteWalletLifecycleService::executeCreate(
    const LiteWalletCreateRequest& request,
    const LiteWalletLifecyclePlan& plan)
{
    auto bridgeCall = bridge_->initializeNew(request.dangerous, plan.server.url);
    return bridgeResult(plan, lifecycleCompletedStatus(), bridgeCall);
}

LiteWalletLifecycleResult LiteWalletLifecycleService::executeOpen(
    const LiteWalletOpenRequest& request,
    const LiteWalletLifecyclePlan& plan)
{
    auto bridgeCall = bridge_->initializeExisting(request.dangerous, plan.server.url);
    return bridgeResult(plan, lifecycleCompletedStatus(), bridgeCall);
}

LiteWalletLifecycleResult LiteWalletLifecycleService::executeRestore(
    const LiteWalletRestoreRequest& request,
    const LiteWalletLifecyclePlan& plan)
{
    auto bridgeCall = bridge_->initializeNewFromPhrase(
        request.dangerous,
        plan.server.url,
        request.seedPhrase,
        request.birthday,
        request.account,
        request.overwrite);
    return bridgeResult(plan, lifecycleCompletedStatus(), bridgeCall);
}

LiteWalletLifecycleResult LiteWalletLifecycleService::blockedResult(
    const LiteWalletLifecyclePlan& plan,
    const WalletBackendStatus& blockedStatus) const
{
    LiteWalletLifecycleResult result;
    result.operation = plan.operation;
    result.plan = plan;
    result.status = blockedStatus;
    result.error = blockedStatus.message;
    return result;
}

LiteWalletLifecycleResult LiteWalletLifecycleService::bridgeResult(
    const LiteWalletLifecyclePlan& plan,
    const WalletBackendStatus& successStatus,
    const LiteBridgeStringResult& bridgeCall) const
{
    LiteWalletLifecycleResult result;
    result.operation = plan.operation;
    result.plan = plan;
    result.attempted = true;
    result.bridgeResponseRedacted = redactLitePrivateDataValue(bridgeCall.ok ? bridgeCall.value : bridgeCall.error);

    if (!bridgeCall.ok) {
        result.status = WalletBackendStatus{
            WalletBackendState::Error,
            "lite wallet lifecycle bridge call failed",
            {},
            {},
            0.0
        };
        result.error = result.status.message;
        return result;
    }

    result.ok = true;
    result.bridgeAccepted = true;
    // The bridge already classifies "Error:"-prefixed responses (and null/empty returns) as
    // failures (ok=false), so reaching here means the backend genuinely succeeded. The success
    // payloads are NOT uniformly JSON: create/restore return a seed object
    // ({"seed":..,"birthday":..}), but open (litelib_initialize_existing) returns the bare
    // string "OK". A JSON-validity test would therefore wrongly mark a successful open as
    // not-ready, so treat any non-empty success response as a ready wallet.
    result.walletReady = !bridgeCall.value.empty();
    result.status = successStatus;
    return result;
}

} // namespace wallet
} // namespace dragonx