#include "wallet/lite_wallet_refresh_readiness_policy.h"

#include <utility>

namespace dragonx::wallet {
namespace {

void addIssue(LiteWalletRefreshReadinessResult& result,
              LiteWalletRefreshReadinessIssue issue,
              std::string message)
{
    result.issues.push_back(LiteWalletRefreshReadinessIssueInfo{issue, std::move(message)});
}

std::string statusMessageOrDefault(const WalletBackendStatus& status,
                                   const std::string& fallback)
{
    return status.message.empty() ? fallback : status.message;
}

LiteWalletRefreshReadinessResult failReadiness(
    LiteWalletRefreshReadinessResult result,
    LiteWalletRefreshReadinessStatus status,
    LiteWalletRefreshReadinessIssue issue,
    std::string message)
{
    result.status = status;
    addIssue(result, issue, std::move(message));
    result.error = result.issues.back().message;
    return result;
}

bool coordinatorReportIsUnsafe(const LiteWalletAppRefreshCoordinationResult& coordination)
{
    return !coordination.dryRunOnly ||
           !coordination.noNetwork ||
           coordination.stateMutationRequested ||
           coordination.stateMutationAllowed ||
           coordination.stateMutated ||
           coordination.walletStateWritten;
}

} // namespace

const char* liteWalletRefreshReadinessStatusName(LiteWalletRefreshReadinessStatus status)
{
    switch (status) {
        case LiteWalletRefreshReadinessStatus::EligibleDryRun: return "EligibleDryRun";
        case LiteWalletRefreshReadinessStatus::CoordinatorRejected: return "CoordinatorRejected";
        case LiteWalletRefreshReadinessStatus::UnsafeReport: return "UnsafeReport";
        case LiteWalletRefreshReadinessStatus::WaitingForLifecycle: return "WaitingForLifecycle";
        case LiteWalletRefreshReadinessStatus::WaitingForSync: return "WaitingForSync";
        case LiteWalletRefreshReadinessStatus::Stale: return "Stale";
    }
    return "Unknown";
}

const char* liteWalletRefreshReadinessIssueName(LiteWalletRefreshReadinessIssue issue)
{
    switch (issue) {
        case LiteWalletRefreshReadinessIssue::CoordinatorReportRejected: return "CoordinatorReportRejected";
        case LiteWalletRefreshReadinessIssue::CoordinatorReportIncomplete: return "CoordinatorReportIncomplete";
        case LiteWalletRefreshReadinessIssue::CoordinatorReportUnsafe: return "CoordinatorReportUnsafe";
        case LiteWalletRefreshReadinessIssue::FullNodeRouteRejected: return "FullNodeRouteRejected";
        case LiteWalletRefreshReadinessIssue::LifecycleNotReady: return "LifecycleNotReady";
        case LiteWalletRefreshReadinessIssue::SyncNotReady: return "SyncNotReady";
        case LiteWalletRefreshReadinessIssue::RefreshTimestampMissing: return "RefreshTimestampMissing";
        case LiteWalletRefreshReadinessIssue::RefreshTimestampInFuture: return "RefreshTimestampInFuture";
        case LiteWalletRefreshReadinessIssue::RefreshStale: return "RefreshStale";
        case LiteWalletRefreshReadinessIssue::StaleRefreshAllowed: return "StaleRefreshAllowed";
    }
    return "Unknown";
}

LiteWalletRefreshReadinessResult evaluateLiteWalletRefreshReadiness(
    const LiteWalletAppRefreshCoordinationResult& coordination,
    const LiteWalletRefreshReadinessInputs& inputs,
    LiteWalletRefreshReadinessPolicyOptions options)
{
    LiteWalletRefreshReadinessResult result;
    result.route = coordination.route;
    result.dryRunOnly = coordination.dryRunOnly;
    result.noNetwork = coordination.noNetwork;
    result.stateMutationRequested = coordination.stateMutationRequested;
    result.stateMutationAllowed = coordination.stateMutationAllowed;
    result.stateMutated = coordination.stateMutated;
    result.walletStateWritten = coordination.walletStateWritten;
    result.lifecycleReady = inputs.lifecycleReady;
    result.lifecycleStatus = inputs.lifecycleStatus;
    result.syncReady = inputs.syncReady;
    result.syncStatus = inputs.syncStatus;
    result.maxRefreshAgeSeconds = options.maxRefreshAgeSeconds;

    if (coordination.fullNodeRefreshDelegated || coordination.route == LiteWalletRefreshRouteKind::FullNodeRpc) {
        return failReadiness(
            std::move(result),
            LiteWalletRefreshReadinessStatus::CoordinatorRejected,
            LiteWalletRefreshReadinessIssue::FullNodeRouteRejected,
            "full-node refresh reports are not eligible for lite UI refresh readiness");
    }

    if (!coordination.ok) {
        return failReadiness(
            std::move(result),
            LiteWalletRefreshReadinessStatus::CoordinatorRejected,
            LiteWalletRefreshReadinessIssue::CoordinatorReportRejected,
            coordination.error.empty()
                ? "lite app refresh coordinator report is not successful"
                : coordination.error);
    }

    result.coordinatorReportAccepted = true;

    if (!coordination.mapped || !coordination.planned || !coordination.executionReported ||
        coordination.successfulCommandCount == 0) {
        return failReadiness(
            std::move(result),
            LiteWalletRefreshReadinessStatus::CoordinatorRejected,
            LiteWalletRefreshReadinessIssue::CoordinatorReportIncomplete,
            "lite app refresh coordinator report is incomplete");
    }

    if (coordinatorReportIsUnsafe(coordination)) {
        return failReadiness(
            std::move(result),
            LiteWalletRefreshReadinessStatus::UnsafeReport,
            LiteWalletRefreshReadinessIssue::CoordinatorReportUnsafe,
            "lite app refresh coordinator report is not a no-network dry-run report");
    }

    if (options.requireLifecycleReady && !inputs.lifecycleReady) {
        return failReadiness(
            std::move(result),
            LiteWalletRefreshReadinessStatus::WaitingForLifecycle,
            LiteWalletRefreshReadinessIssue::LifecycleNotReady,
            statusMessageOrDefault(inputs.lifecycleStatus, "lite wallet lifecycle is not ready"));
    }

    if (options.requireSyncReady && !inputs.syncReady) {
        return failReadiness(
            std::move(result),
            LiteWalletRefreshReadinessStatus::WaitingForSync,
            LiteWalletRefreshReadinessIssue::SyncNotReady,
            statusMessageOrDefault(inputs.syncStatus, "lite wallet sync is not ready"));
    }

    if (options.requireFreshRefresh) {
        if (!inputs.freshness.haveSnapshotTime) {
            return failReadiness(
                std::move(result),
                LiteWalletRefreshReadinessStatus::Stale,
                LiteWalletRefreshReadinessIssue::RefreshTimestampMissing,
                "lite refresh snapshot time is not known");
        }

        if (inputs.freshness.snapshotTimeSeconds > inputs.freshness.nowSeconds) {
            return failReadiness(
                std::move(result),
                LiteWalletRefreshReadinessStatus::Stale,
                LiteWalletRefreshReadinessIssue::RefreshTimestampInFuture,
                "lite refresh snapshot time is in the future");
        }

        result.refreshAgeSeconds = inputs.freshness.nowSeconds - inputs.freshness.snapshotTimeSeconds;
        if (result.refreshAgeSeconds > options.maxRefreshAgeSeconds) {
            result.refreshStale = true;
            if (!options.allowStaleRefresh) {
                return failReadiness(
                    std::move(result),
                    LiteWalletRefreshReadinessStatus::Stale,
                    LiteWalletRefreshReadinessIssue::RefreshStale,
                    "lite refresh coordinator report is stale");
            }

            result.staleRefreshAllowed = true;
            addIssue(result,
                     LiteWalletRefreshReadinessIssue::StaleRefreshAllowed,
                     "stale lite refresh coordinator report is allowed for reporting only");
        } else {
            result.refreshFresh = true;
        }
    } else {
        result.refreshFresh = true;
    }

    result.ok = true;
    result.eligibleForFutureUiRefresh = true;
    result.status = LiteWalletRefreshReadinessStatus::EligibleDryRun;
    return result;
}

LiteWalletRefreshReadinessPolicy::LiteWalletRefreshReadinessPolicy(
    LiteWalletRefreshReadinessPolicyOptions options)
    : options_(options)
{
}

LiteWalletRefreshReadinessResult LiteWalletRefreshReadinessPolicy::evaluate(
    const LiteWalletAppRefreshCoordinationResult& coordination,
    const LiteWalletRefreshReadinessInputs& inputs) const
{
    return evaluateLiteWalletRefreshReadiness(coordination, inputs, options_);
}

} // namespace dragonx::wallet