#include "wallet/lite_wallet_app_refresh_orchestrator.h"

#include <utility>

namespace dragonx::wallet {
namespace {

void addIssue(LiteWalletAppRefreshOrchestrationResult& result,
              LiteWalletAppRefreshOrchestrationIssue issue,
              std::string message)
{
    result.issues.push_back(LiteWalletAppRefreshOrchestrationIssueInfo{issue, std::move(message)});
}

bool readinessReportIsUnsafe(const LiteWalletRefreshReadinessResult& readiness)
{
    return !readiness.dryRunOnly ||
           !readiness.noNetwork ||
           readiness.stateMutationRequested ||
           readiness.stateMutationAllowed ||
           readiness.stateMutated ||
           readiness.walletStateWritten;
}

std::string readinessErrorOrDefault(const LiteWalletRefreshReadinessResult& readiness)
{
    return readiness.error.empty()
        ? "lite refresh readiness report is not eligible"
        : readiness.error;
}

LiteWalletAppRefreshOrchestrationResult blockedResult(
    LiteWalletAppRefreshOrchestrationResult result,
    LiteWalletAppRefreshOrchestrationIssue issue,
    std::string message)
{
    result.status = LiteWalletAppRefreshOrchestrationStatus::Blocked;
    result.blocked = true;
    addIssue(result, issue, std::move(message));
    result.error = result.issues.back().message;
    return result;
}

LiteWalletAppRefreshOrchestrationResult skippedResult(
    LiteWalletAppRefreshOrchestrationResult result,
    LiteWalletAppRefreshOrchestrationIssue issue,
    std::string message)
{
    result.ok = true;
    result.status = LiteWalletAppRefreshOrchestrationStatus::Skipped;
    result.skipped = true;
    addIssue(result, issue, std::move(message));
    return result;
}

std::size_t effectiveMaxQueueDepth(const LiteWalletAppRefreshScheduleInput& schedule,
                                   const LiteWalletAppRefreshOrchestratorOptions& options)
{
    return schedule.maxQueueDepth > 0 ? schedule.maxQueueDepth : options.maxQueueDepth;
}

} // namespace

const char* liteWalletAppRefreshOrchestrationStatusName(
    LiteWalletAppRefreshOrchestrationStatus status)
{
    switch (status) {
        case LiteWalletAppRefreshOrchestrationStatus::Queued: return "Queued";
        case LiteWalletAppRefreshOrchestrationStatus::Skipped: return "Skipped";
        case LiteWalletAppRefreshOrchestrationStatus::Blocked: return "Blocked";
    }
    return "Unknown";
}

const char* liteWalletAppRefreshScheduleTriggerName(
    LiteWalletAppRefreshScheduleTrigger trigger)
{
    switch (trigger) {
        case LiteWalletAppRefreshScheduleTrigger::Periodic: return "Periodic";
        case LiteWalletAppRefreshScheduleTrigger::Manual: return "Manual";
        case LiteWalletAppRefreshScheduleTrigger::Startup: return "Startup";
        case LiteWalletAppRefreshScheduleTrigger::WalletOpened: return "WalletOpened";
    }
    return "Unknown";
}

const char* liteWalletAppRefreshOrchestrationIssueName(
    LiteWalletAppRefreshOrchestrationIssue issue)
{
    switch (issue) {
        case LiteWalletAppRefreshOrchestrationIssue::ReadinessRejected: return "ReadinessRejected";
        case LiteWalletAppRefreshOrchestrationIssue::UnsafeReadinessReport: return "UnsafeReadinessReport";
        case LiteWalletAppRefreshOrchestrationIssue::SchedulerDisabled: return "SchedulerDisabled";
        case LiteWalletAppRefreshOrchestrationIssue::RefreshNotDue: return "RefreshNotDue";
        case LiteWalletAppRefreshOrchestrationIssue::RefreshAlreadyQueued: return "RefreshAlreadyQueued";
        case LiteWalletAppRefreshOrchestrationIssue::RefreshInProgress: return "RefreshInProgress";
        case LiteWalletAppRefreshOrchestrationIssue::QueuePressure: return "QueuePressure";
    }
    return "Unknown";
}

LiteWalletAppRefreshOrchestrationResult orchestrateLiteWalletAppRefresh(
    const LiteWalletRefreshReadinessResult& readiness,
    const LiteWalletAppRefreshScheduleInput& schedule,
    LiteWalletAppRefreshOrchestratorOptions options)
{
    LiteWalletAppRefreshOrchestrationResult result;
    result.readinessResult = readiness;
    result.route = readiness.route;
    result.dryRunOnly = readiness.dryRunOnly;
    result.noNetwork = readiness.noNetwork;
    result.stateMutationRequested = readiness.stateMutationRequested;
    result.stateMutationAllowed = readiness.stateMutationAllowed;
    result.stateMutated = readiness.stateMutated;
    result.walletStateWritten = readiness.walletStateWritten;
    result.trigger = schedule.trigger;
    result.schedulerEnabled = schedule.schedulerEnabled;
    result.refreshDue = schedule.refreshDue;
    result.forceRefresh = schedule.forceRefresh;
    result.refreshAlreadyQueued = schedule.refreshAlreadyQueued;
    result.refreshInProgress = schedule.refreshInProgress;
    result.queueDepth = schedule.queueDepth;
    result.maxQueueDepth = effectiveMaxQueueDepth(schedule, options);

    if (options.requireEligibleReadiness && (!readiness.ok || !readiness.eligibleForFutureUiRefresh)) {
        return blockedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::ReadinessRejected,
            readinessErrorOrDefault(readiness));
    }

    if (readinessReportIsUnsafe(readiness)) {
        return blockedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::UnsafeReadinessReport,
            "lite refresh readiness report is not a no-network dry-run report");
    }

    result.readinessAccepted = true;

    if (!schedule.schedulerEnabled) {
        return blockedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::SchedulerDisabled,
            "lite app refresh scheduler is disabled");
    }

    result.schedulerAccepted = true;

    if (result.maxQueueDepth > 0 && schedule.queueDepth >= result.maxQueueDepth) {
        return blockedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::QueuePressure,
            "lite app refresh queue is at capacity");
    }

    if (schedule.refreshAlreadyQueued) {
        return skippedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::RefreshAlreadyQueued,
            "lite app refresh is already queued");
    }

    if (schedule.refreshInProgress) {
        return skippedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::RefreshInProgress,
            "lite app refresh is already in progress");
    }

    if (!schedule.refreshDue && !schedule.forceRefresh) {
        return skippedResult(
            std::move(result),
            LiteWalletAppRefreshOrchestrationIssue::RefreshNotDue,
            "lite app refresh is not due");
    }

    result.ok = true;
    result.status = LiteWalletAppRefreshOrchestrationStatus::Queued;
    result.wouldQueueRefresh = true;
    return result;
}

LiteWalletAppRefreshOrchestrator::LiteWalletAppRefreshOrchestrator(
    LiteWalletAppRefreshOrchestratorOptions options)
    : options_(options)
{
}

LiteWalletAppRefreshOrchestrationResult LiteWalletAppRefreshOrchestrator::evaluate(
    const LiteWalletRefreshReadinessResult& readiness,
    const LiteWalletAppRefreshScheduleInput& schedule) const
{
    return orchestrateLiteWalletAppRefresh(readiness, schedule, options_);
}

} // namespace dragonx::wallet