#include "wallet/lite_wallet_gateway.h"

#include <utility>

namespace dragonx::wallet {
namespace {

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

WalletBackendStatus refreshStatusForRequest(const LiteWalletRefreshRequest& request)
{
    if (request.haveSyncStatus) return walletStatusFromLiteSyncStatus(request.syncStatus);
    return makeGatewayStatus(
        WalletBackendState::Disconnected,
        "lite wallet refresh bundle assembled; runtime wallet state is unchanged");
}

LiteWalletGatewayCommandResult parsedCommandResult(const LiteWalletGatewayPlan& plan)
{
    LiteWalletGatewayCommandResult result;
    result.ok = true;
    result.attempted = true;
    result.bridgeAccepted = true;
    result.plan = plan;
    result.parserError = LiteResultParserError::None;
    result.status = makeGatewayStatus(
        WalletBackendState::Disconnected,
        "lite gateway response parsed into intermediate refresh model");
    result.bridgeResponseRedacted = "<redacted>";
    return result;
}

} // namespace

const char* liteWalletGatewayCommandName(LiteWalletGatewayCommand command)
{
    switch (command) {
        case LiteWalletGatewayCommand::Info: return "info";
        case LiteWalletGatewayCommand::Height: return "height";
        case LiteWalletGatewayCommand::Balance: return "balance";
        case LiteWalletGatewayCommand::Addresses: return "addresses";
        case LiteWalletGatewayCommand::Notes: return "notes";
        case LiteWalletGatewayCommand::List: return "list";
    }
    return "unknown";
}

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

std::vector<LiteWalletGatewayCommand> liteWalletRefreshCommands(const LiteWalletRefreshRequest& request)
{
    std::vector<LiteWalletGatewayCommand> commands;
    if (request.includeInfo) commands.push_back(LiteWalletGatewayCommand::Info);
    if (request.includeHeight) commands.push_back(LiteWalletGatewayCommand::Height);
    if (request.includeBalance) commands.push_back(LiteWalletGatewayCommand::Balance);
    if (request.includeAddresses) commands.push_back(LiteWalletGatewayCommand::Addresses);
    if (request.includeNotes) commands.push_back(LiteWalletGatewayCommand::Notes);
    if (request.includeTransactions) commands.push_back(LiteWalletGatewayCommand::List);
    return commands;
}

LiteWalletRefreshBundle assembleLiteWalletRefreshBundle(const std::vector<LiteWalletGatewayCommandResult>& results,
                                                        const LiteWalletRefreshRequest& request)
{
    LiteWalletRefreshBundle bundle;
    if (request.haveSyncStatus) {
        bundle.hasSyncStatus = true;
        bundle.syncStatus = request.syncStatus;
    }

    for (const auto& result : results) {
        if (!result.ok) continue;
        ++bundle.successfulCommandCount;
        switch (result.plan.command) {
            case LiteWalletGatewayCommand::Info:
                bundle.hasInfo = true;
                bundle.info = result.info;
                break;
            case LiteWalletGatewayCommand::Height:
                bundle.hasHeight = true;
                bundle.height = result.height;
                break;
            case LiteWalletGatewayCommand::Balance:
                bundle.hasBalance = true;
                bundle.balance = result.balance;
                break;
            case LiteWalletGatewayCommand::Addresses:
                bundle.hasAddresses = true;
                bundle.addresses = result.addresses;
                break;
            case LiteWalletGatewayCommand::Notes:
                bundle.hasNotes = true;
                bundle.notes = result.notes;
                break;
            case LiteWalletGatewayCommand::List:
                bundle.hasTransactions = true;
                bundle.transactions = result.transactions;
                break;
        }
    }

    const auto expectedCommands = liteWalletRefreshCommands(request);
    bundle.complete = bundle.successfulCommandCount == expectedCommands.size();
    return bundle;
}

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

LiteWalletGatewayAvailability LiteWalletGateway::availability() const
{
    if (!isLiteBuild(capabilities_)) return LiteWalletGatewayAvailability::UnsupportedBuild;
    if (!supportsLiteBackend(capabilities_)) return LiteWalletGatewayAvailability::BackendUnavailable;
    if (!bridge_ || !bridge_->available()) return LiteWalletGatewayAvailability::BridgeUnavailable;
    if (!selectLiteServer(connectionSettings_).ok) return LiteWalletGatewayAvailability::NoUsableServer;
    if (!options_.allowBridgeCalls) return LiteWalletGatewayAvailability::BridgeCallsDisabled;
    return LiteWalletGatewayAvailability::Ready;
}

WalletBackendStatus LiteWalletGateway::status() const
{
    const auto currentAvailability = availability();
    if (currentAvailability == LiteWalletGatewayAvailability::NoUsableServer) {
        return statusFor(currentAvailability, selectLiteServer(connectionSettings_).error);
    }
    return statusFor(currentAvailability);
}

LiteWalletGatewayPlan LiteWalletGateway::planCommand(const LiteWalletGatewayRequest& request) const
{
    LiteWalletGatewayPlan plan;
    plan.command = request.command;
    plan.commandName = liteWalletGatewayCommandName(request.command);
    plan.bridgeExecutionAllowed = options_.allowBridgeCalls;

    auto selection = selectServerForRequest(request.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;
}

LiteWalletRefreshPlan LiteWalletGateway::planRefresh(const LiteWalletRefreshRequest& request) const
{
    LiteWalletRefreshPlan plan;
    const auto commands = liteWalletRefreshCommands(request);
    if (commands.empty()) {
        plan.error = "lite refresh request has no commands";
        return plan;
    }

    for (const auto command : commands) {
        const auto commandPlan = planCommand(LiteWalletGatewayRequest{command, request.serverUrl});
        if (!commandPlan.ok) {
            plan.error = commandPlan.error;
            return plan;
        }
        plan.commands.push_back(commandPlan);
    }

    plan.ok = true;
    return plan;
}

LiteWalletGatewayCommandResult LiteWalletGateway::fetchCommand(const LiteWalletGatewayRequest& request)
{
    const auto plan = planCommand(request);
    if (!plan.ok) return blockedCommandResult(plan, makeGatewayStatus(WalletBackendState::Error, plan.error));
    if (availability() != LiteWalletGatewayAvailability::Ready) return blockedCommandResult(plan, status());
    return executePlannedCommand(plan);
}

LiteWalletRefreshResult LiteWalletGateway::refresh(const LiteWalletRefreshRequest& request)
{
    LiteWalletRefreshResult result;
    result.plan = planRefresh(request);
    if (!result.plan.ok) {
        result.status = makeGatewayStatus(WalletBackendState::Error, result.plan.error);
        result.error = result.status.message;
        return result;
    }

    if (availability() != LiteWalletGatewayAvailability::Ready) {
        result.status = status();
        result.error = result.status.message;
        return result;
    }

    result.attempted = true;
    // Run every planned command. A single command's bridge or parse failure must NOT abort the
    // whole refresh — partial data beats none, and real-backend response shapes can drift per
    // command. assembleLiteWalletRefreshBundle() ignores the failed results.
    for (const auto& commandPlan : result.plan.commands) {
        result.commandResults.push_back(executePlannedCommand(commandPlan));
    }

    result.bundle = assembleLiteWalletRefreshBundle(result.commandResults, request);

    // Succeed if anything usable came back; some commands may have been skipped. (bundle.complete
    // still reflects whether *all* commands succeeded, for callers that care.)
    result.ok = result.bundle.successfulCommandCount > 0 || result.bundle.hasSyncStatus;
    if (!result.ok) {
        for (const auto& commandResult : result.commandResults) {
            if (!commandResult.ok) {
                result.status = commandResult.status;
                result.error = commandResult.error;
                break;
            }
        }
        if (result.error.empty()) {
            result.status = makeGatewayStatus(WalletBackendState::Error, "lite refresh produced no usable data");
            result.error = result.status.message;
        }
        return result;
    }
    result.status = refreshStatusForRequest(request);
    return result;
}

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

WalletBackendStatus LiteWalletGateway::statusFor(LiteWalletGatewayAvailability availability,
                                                 const std::string& detail) const
{
    switch (availability) {
        case LiteWalletGatewayAvailability::Ready:
            return makeGatewayStatus(
                WalletBackendState::Disconnected,
                detail.empty() ? "lite wallet gateway scaffold ready; refresh execution is fake-test only" : detail);
        case LiteWalletGatewayAvailability::UnsupportedBuild:
            return makeGatewayStatus(
                WalletBackendState::Unavailable,
                "lite wallet gateway is unsupported in full-node builds");
        case LiteWalletGatewayAvailability::BackendUnavailable:
            return makeGatewayStatus(
                WalletBackendState::Unavailable,
                "lite backend is not linked");
        case LiteWalletGatewayAvailability::BridgeUnavailable:
            return makeGatewayStatus(
                WalletBackendState::Unavailable,
                detail.empty() ? (bridge_ ? bridge_->unavailableReason() : "lite bridge is unavailable") : detail);
        case LiteWalletGatewayAvailability::BridgeCallsDisabled:
            return makeGatewayStatus(
                WalletBackendState::Unavailable,
                "lite wallet gateway bridge calls are disabled");
        case LiteWalletGatewayAvailability::NoUsableServer:
            return makeGatewayStatus(
                WalletBackendState::Error,
                detail.empty() ? "no usable lite servers are configured" : detail);
    }

    return makeGatewayStatus(WalletBackendState::Unavailable, "unknown lite wallet gateway state");
}

LiteWalletGatewayCommandResult LiteWalletGateway::blockedCommandResult(
    const LiteWalletGatewayPlan& plan,
    const WalletBackendStatus& blockedStatus) const
{
    LiteWalletGatewayCommandResult result;
    result.plan = plan;
    result.status = blockedStatus;
    result.error = blockedStatus.message;
    return result;
}

LiteWalletGatewayCommandResult LiteWalletGateway::executePlannedCommand(const LiteWalletGatewayPlan& plan)
{
    LiteWalletGatewayCommandResult result;
    result.plan = plan;
    result.attempted = true;

    const auto bridgeCall = bridge_->execute(plan.commandName, plan.args);
    result.bridgeResponseRedacted = bridgeCall.ok || !bridgeCall.error.empty() ? "<redacted>" : "<empty>";
    if (!bridgeCall.ok) {
        result.status = makeGatewayStatus(WalletBackendState::Error, "lite wallet gateway bridge call failed");
        result.error = result.status.message;
        return result;
    }

    return parseBridgeResponse(plan, bridgeCall.value);
}

LiteWalletGatewayCommandResult LiteWalletGateway::parseBridgeResponse(
    const LiteWalletGatewayPlan& plan,
    const std::string& response) const
{
    auto result = parsedCommandResult(plan);

    switch (plan.command) {
        case LiteWalletGatewayCommand::Info: {
            const auto parsed = parseLiteInfoResponse(response);
            if (!parsed.ok) {
                result.ok = false;
                result.parserError = parsed.error;
                break;
            }
            result.info = parsed.info;
            return result;
        }
        case LiteWalletGatewayCommand::Height: {
            const auto parsed = parseLiteHeightResponse(response);
            if (!parsed.ok) {
                result.ok = false;
                result.parserError = parsed.error;
                break;
            }
            result.height = parsed.height;
            return result;
        }
        case LiteWalletGatewayCommand::Balance: {
            const auto parsed = parseLiteBalanceResponse(response);
            if (!parsed.ok) {
                result.ok = false;
                result.parserError = parsed.error;
                break;
            }
            result.balance = parsed.balance;
            return result;
        }
        case LiteWalletGatewayCommand::Addresses: {
            const auto parsed = parseLiteAddressesResponse(response);
            if (!parsed.ok) {
                result.ok = false;
                result.parserError = parsed.error;
                break;
            }
            result.addresses = parsed.addresses;
            return result;
        }
        case LiteWalletGatewayCommand::Notes: {
            const auto parsed = parseLiteNotesResponse(response);
            if (!parsed.ok) {
                result.ok = false;
                result.parserError = parsed.error;
                break;
            }
            result.notes = parsed.notes;
            return result;
        }
        case LiteWalletGatewayCommand::List: {
            const auto parsed = parseLiteTransactionsResponse(response);
            if (!parsed.ok) {
                result.ok = false;
                result.parserError = parsed.error;
                break;
            }
            result.transactions = parsed.transactions;
            return result;
        }
    }

    result.bridgeAccepted = true;
    result.status = makeGatewayStatus(WalletBackendState::Error, "lite wallet gateway response could not be parsed");
    result.error = result.status.message;
    result.bridgeResponseRedacted = "<redacted>";
    return result;
}

} // namespace dragonx::wallet