#include "wallet/lite_wallet_refresh_service.h"

#include <utility>

namespace dragonx::wallet {
namespace {

WalletBackendStatus makeRefreshStatus(WalletBackendState state, std::string message)
{
    WalletBackendStatus status;
    status.state = state;
    status.message = std::move(message);
    return status;
}

} // namespace

LiteWalletGatewayRefreshAdapter::LiteWalletGatewayRefreshAdapter(LiteWalletGateway& gateway)
    : gateway_(gateway)
{
}

LiteWalletRefreshPlan LiteWalletGatewayRefreshAdapter::planRefresh(
    const LiteWalletRefreshRequest& request) const
{
    return gateway_.planRefresh(request);
}

LiteWalletRefreshResult LiteWalletGatewayRefreshAdapter::refresh(
    const LiteWalletRefreshRequest& request)
{
    return gateway_.refresh(request);
}

WalletBackendStatus LiteWalletGatewayRefreshAdapter::status() const
{
    return gateway_.status();
}

const char* liteWalletRefreshRouteKindName(LiteWalletRefreshRouteKind route)
{
    switch (route) {
        case LiteWalletRefreshRouteKind::FullNodeRpc: return "FullNodeRpc";
        case LiteWalletRefreshRouteKind::LiteGateway: return "LiteGateway";
        case LiteWalletRefreshRouteKind::Unavailable: return "Unavailable";
    }
    return "Unknown";
}

LiteWalletRefreshRouteKind liteWalletRefreshRouteForCapabilities(const WalletCapabilities& capabilities)
{
    if (capabilities.fullNodeRpcAvailable) return LiteWalletRefreshRouteKind::FullNodeRpc;
    if (isLiteBuild(capabilities) && supportsLiteBackend(capabilities)) {
        return LiteWalletRefreshRouteKind::LiteGateway;
    }
    return LiteWalletRefreshRouteKind::Unavailable;
}

LiteWalletRefreshService::LiteWalletRefreshService(WalletCapabilities capabilities,
                                                   LiteWalletRefreshGateway* liteGateway,
                                                   LiteWalletRefreshServiceOptions options)
    : capabilities_(capabilities), liteGateway_(liteGateway), options_(options)
{
}

LiteWalletRefreshRouteKind LiteWalletRefreshService::route() const
{
    return liteWalletRefreshRouteForCapabilities(capabilities_);
}

WalletBackendStatus LiteWalletRefreshService::status() const
{
    const auto selectedRoute = route();
    if (selectedRoute == LiteWalletRefreshRouteKind::LiteGateway) {
        if (!liteGateway_) return statusForRoute(selectedRoute, "lite wallet gateway is not configured");
        return liteGateway_->status();
    }
    return statusForRoute(selectedRoute);
}

LiteWalletRefreshServicePlan LiteWalletRefreshService::planRefresh(
    const LiteWalletRefreshRequest& request) const
{
    LiteWalletRefreshServicePlan plan;
    plan.route = route();

    if (plan.route == LiteWalletRefreshRouteKind::FullNodeRpc) {
        plan.ok = true;
        plan.fullNodeRefreshDelegated = true;
        plan.status = statusForRoute(plan.route);
        return plan;
    }

    if (plan.route == LiteWalletRefreshRouteKind::Unavailable) {
        plan.status = statusForRoute(plan.route);
        plan.error = plan.status.message;
        return plan;
    }

    if (!liteGateway_) {
        plan.status = statusForRoute(plan.route, "lite wallet gateway is not configured");
        plan.error = plan.status.message;
        return plan;
    }

    plan.liteGatewayConfigured = true;
    plan.liteGatewayExecutionAllowed = options_.allowLiteGatewayRefresh;
    plan.liteGatewayPlan = liteGateway_->planRefresh(request);
    if (!plan.liteGatewayPlan.ok) {
        plan.status = makeRefreshStatus(WalletBackendState::Error, plan.liteGatewayPlan.error);
        plan.error = plan.status.message;
        return plan;
    }

    plan.ok = true;
    plan.status = liteGateway_->status();
    return plan;
}

LiteWalletRefreshServiceResult LiteWalletRefreshService::refresh(
    const LiteWalletRefreshRequest& request)
{
    auto plan = planRefresh(request);
    if (!plan.ok) return resultFromPlan(plan);

    if (plan.route == LiteWalletRefreshRouteKind::FullNodeRpc) {
        auto result = resultFromPlan(plan);
        result.ok = true;
        return result;
    }

    if (!plan.liteGatewayExecutionAllowed) {
        plan.status = makeRefreshStatus(
            WalletBackendState::Unavailable,
            "lite wallet refresh gateway execution is disabled");
        plan.error = plan.status.message;
        return resultFromPlan(plan);
    }

    LiteWalletRefreshServiceResult result;
    result.plan = plan;
    result.liteGatewayCalled = true;
    result.liteGatewayResult = liteGateway_->refresh(request);
    result.attempted = result.liteGatewayResult.attempted;
    result.bundle = result.liteGatewayResult.bundle;
    result.status = result.liteGatewayResult.status;
    result.error = result.liteGatewayResult.error;
    result.ok = result.liteGatewayResult.ok;
    return result;
}

WalletBackendStatus LiteWalletRefreshService::statusForRoute(
    LiteWalletRefreshRouteKind selectedRoute,
    const std::string& detail) const
{
    switch (selectedRoute) {
        case LiteWalletRefreshRouteKind::FullNodeRpc:
            return makeRefreshStatus(
                WalletBackendState::Disconnected,
                detail.empty()
                    ? "full-node refresh remains handled by existing NetworkRefreshService"
                    : detail);
        case LiteWalletRefreshRouteKind::LiteGateway:
            return makeRefreshStatus(
                detail.empty() ? WalletBackendState::Disconnected : WalletBackendState::Unavailable,
                detail.empty()
                    ? "lite wallet refresh route is ready for gateway planning"
                    : detail);
        case LiteWalletRefreshRouteKind::Unavailable:
            return makeRefreshStatus(
                WalletBackendState::Unavailable,
                detail.empty() ? "lite backend is not linked" : detail);
    }

    return makeRefreshStatus(WalletBackendState::Unavailable, "unknown wallet refresh route");
}

LiteWalletRefreshServiceResult LiteWalletRefreshService::resultFromPlan(
    const LiteWalletRefreshServicePlan& plan) const
{
    LiteWalletRefreshServiceResult result;
    result.plan = plan;
    result.fullNodeRefreshDelegated = plan.fullNodeRefreshDelegated;
    result.status = plan.status;
    result.error = plan.error;
    return result;
}

} // namespace dragonx::wallet