#include "wallet/lite_wallet_sync_app_refresh_integration.h"

#include <utility>

namespace dragonx::wallet {
namespace {

void addIssue(LiteWalletSyncAppRefreshIntegrationResult& result,
              LiteWalletSyncAppRefreshIntegrationIssue issue,
              std::string message)
{
    result.issues.push_back(LiteWalletSyncAppRefreshIntegrationIssueInfo{issue, std::move(message)});
}

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

bool refreshReportIsUnsafe(const LiteWalletAppRefreshOrchestrationResult& refresh)
{
    return !refresh.dryRunOnly ||
           !refresh.noNetwork ||
           refresh.stateMutationRequested ||
           refresh.stateMutationAllowed ||
           refresh.stateMutated ||
           refresh.walletStateWritten;
}

bool syncStartPlanIsValid(const LiteSyncPlan& plan)
{
    return plan.ok && plan.operation == LiteSyncOperation::StartSync && plan.command == "sync";
}

bool syncStatusPlanIsValid(const LiteSyncPlan& plan)
{
    return plan.ok && plan.operation == LiteSyncOperation::PollSyncStatus && plan.command == "syncstatus";
}

LiteWalletSyncAppRefreshIntegrationResult stoppedResult(
    LiteWalletSyncAppRefreshIntegrationResult result,
    LiteWalletSyncAppRefreshIntegrationStatus status,
    LiteWalletSyncAppRefreshIntegrationIssue issue,
    std::string message)
{
    result.status = status;
    addIssue(result, issue, std::move(message));
    result.error = result.issues.back().message;
    return result;
}

LiteWalletSyncAppRefreshIntegrationResult skippedResult(
    LiteWalletSyncAppRefreshIntegrationResult result,
    LiteWalletSyncAppRefreshIntegrationIssue issue,
    std::string message)
{
    result.ok = true;
    result.status = LiteWalletSyncAppRefreshIntegrationStatus::RefreshNotQueued;
    addIssue(result, issue, std::move(message));
    return result;
}

void copyRecoveryFlags(LiteWalletSyncAppRefreshIntegrationResult& result,
                       const LiteSyncRecoveryDecision& decision)
{
    result.recoveryDecision = decision;
    result.futureClearRequired = decision.shouldClear;
    result.futureRescanRequired = decision.shouldRescan;
    result.futureRestartSyncRequired = decision.shouldRestartSync;
    result.requiresUserAttention = decision.requiresUserAttention;
}

} // namespace

const char* liteWalletSyncAppRefreshIntegrationStatusName(
    LiteWalletSyncAppRefreshIntegrationStatus status)
{
    switch (status) {
        case LiteWalletSyncAppRefreshIntegrationStatus::FutureWorkerQueueFeedReady: return "FutureWorkerQueueFeedReady";
        case LiteWalletSyncAppRefreshIntegrationStatus::WaitingForLifecycle: return "WaitingForLifecycle";
        case LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncPlan: return "WaitingForSyncPlan";
        case LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncStatus: return "WaitingForSyncStatus";
        case LiteWalletSyncAppRefreshIntegrationStatus::WaitingForRecovery: return "WaitingForRecovery";
        case LiteWalletSyncAppRefreshIntegrationStatus::WaitingForCancellation: return "WaitingForCancellation";
        case LiteWalletSyncAppRefreshIntegrationStatus::RefreshNotQueued: return "RefreshNotQueued";
        case LiteWalletSyncAppRefreshIntegrationStatus::WorkerQueueUnavailable: return "WorkerQueueUnavailable";
        case LiteWalletSyncAppRefreshIntegrationStatus::UnsafePlan: return "UnsafePlan";
    }
    return "Unknown";
}

const char* liteWalletSyncAppRefreshIntegrationIssueName(
    LiteWalletSyncAppRefreshIntegrationIssue issue)
{
    switch (issue) {
        case LiteWalletSyncAppRefreshIntegrationIssue::LifecycleNotReady: return "LifecycleNotReady";
        case LiteWalletSyncAppRefreshIntegrationIssue::MissingSyncStartPlan: return "MissingSyncStartPlan";
        case LiteWalletSyncAppRefreshIntegrationIssue::InvalidSyncStartPlan: return "InvalidSyncStartPlan";
        case LiteWalletSyncAppRefreshIntegrationIssue::SyncStartWouldExecute: return "SyncStartWouldExecute";
        case LiteWalletSyncAppRefreshIntegrationIssue::MissingSyncStatusPlan: return "MissingSyncStatusPlan";
        case LiteWalletSyncAppRefreshIntegrationIssue::InvalidSyncStatusPlan: return "InvalidSyncStatusPlan";
        case LiteWalletSyncAppRefreshIntegrationIssue::SyncStatusWouldExecute: return "SyncStatusWouldExecute";
        case LiteWalletSyncAppRefreshIntegrationIssue::MissingRecoveryDecision: return "MissingRecoveryDecision";
        case LiteWalletSyncAppRefreshIntegrationIssue::SyncStillPolling: return "SyncStillPolling";
        case LiteWalletSyncAppRefreshIntegrationIssue::SyncRecoveryRequired: return "SyncRecoveryRequired";
        case LiteWalletSyncAppRefreshIntegrationIssue::SyncRecoveryNeedsUserAttention: return "SyncRecoveryNeedsUserAttention";
        case LiteWalletSyncAppRefreshIntegrationIssue::CancellationRequired: return "CancellationRequired";
        case LiteWalletSyncAppRefreshIntegrationIssue::CancellationUnsupported: return "CancellationUnsupported";
        case LiteWalletSyncAppRefreshIntegrationIssue::RefreshReportRejected: return "RefreshReportRejected";
        case LiteWalletSyncAppRefreshIntegrationIssue::RefreshReportUnsafe: return "RefreshReportUnsafe";
        case LiteWalletSyncAppRefreshIntegrationIssue::RefreshSkipped: return "RefreshSkipped";
        case LiteWalletSyncAppRefreshIntegrationIssue::RefreshBlocked: return "RefreshBlocked";
        case LiteWalletSyncAppRefreshIntegrationIssue::FutureWorkerQueueDisabled: return "FutureWorkerQueueDisabled";
        case LiteWalletSyncAppRefreshIntegrationIssue::FutureWorkerQueuePressure: return "FutureWorkerQueuePressure";
    }
    return "Unknown";
}

LiteWalletSyncAppRefreshIntegrationResult planLiteWalletSyncAppRefreshIntegration(
    const LiteWalletSyncAppRefreshIntegrationInput& input,
    LiteWalletSyncAppRefreshIntegrationOptions options)
{
    LiteWalletSyncAppRefreshIntegrationResult result;
    result.lifecycleStatus = input.lifecycle.status;
    result.refreshOrchestration = input.refreshOrchestration;
    result.route = input.refreshOrchestration.route;
    result.refreshWouldQueue = input.refreshOrchestration.wouldQueueRefresh;
    result.refreshSkipped = input.refreshOrchestration.skipped;
    result.refreshBlocked = input.refreshOrchestration.blocked;
    result.futureWorkerQueuePlan.laneName = input.futureWorkerQueue.laneName;
    result.futureWorkerQueuePlan.trigger = input.refreshOrchestration.trigger;
    result.futureWorkerQueuePlan.route = input.refreshOrchestration.route;
    result.futureWorkerQueuePlan.queueDepth = input.futureWorkerQueue.queueDepth;
    result.futureWorkerQueuePlan.maxQueueDepth = input.futureWorkerQueue.maxQueueDepth;

    if (input.sync.haveStartPlan) {
        result.syncStartPlan = input.sync.startPlan;
        result.futureWorkerQueuePlan.startSyncCommand = input.sync.startPlan.command;
    }
    if (input.sync.haveStatusPlan) {
        result.syncStatusPlan = input.sync.statusPlan;
        result.futureWorkerQueuePlan.syncStatusCommand = input.sync.statusPlan.command;
    }
    if (input.sync.haveRecoveryDecision) {
        copyRecoveryFlags(result, input.sync.recoveryDecision);
    }

    if (options.requireLifecycleReady && !input.lifecycle.ready) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WaitingForLifecycle,
            LiteWalletSyncAppRefreshIntegrationIssue::LifecycleNotReady,
            statusMessageOrDefault(input.lifecycle.status, "lite wallet lifecycle is not ready"));
    }
    result.lifecycleAccepted = true;

    if (options.requireSyncStartPlan && !input.sync.haveStartPlan) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncPlan,
            LiteWalletSyncAppRefreshIntegrationIssue::MissingSyncStartPlan,
            "lite sync start plan is required before app refresh integration");
    }

    if (input.sync.haveStartPlan) {
        if (!syncStartPlanIsValid(input.sync.startPlan)) {
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncPlan,
                LiteWalletSyncAppRefreshIntegrationIssue::InvalidSyncStartPlan,
                input.sync.startPlan.error.empty()
                    ? "lite sync start plan is invalid"
                    : input.sync.startPlan.error);
        }
        if (options.rejectExecutableSyncPlans && input.sync.startPlan.bridgeExecutionAllowed) {
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::UnsafePlan,
                LiteWalletSyncAppRefreshIntegrationIssue::SyncStartWouldExecute,
                "lite sync start plan would allow bridge execution");
        }
        result.syncStartPlanAccepted = true;
    }

    if (options.requireSyncStatusPlan && !input.sync.haveStatusPlan) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncStatus,
            LiteWalletSyncAppRefreshIntegrationIssue::MissingSyncStatusPlan,
            "lite syncstatus plan is required before app refresh integration");
    }

    if (input.sync.haveStatusPlan) {
        if (!syncStatusPlanIsValid(input.sync.statusPlan)) {
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncStatus,
                LiteWalletSyncAppRefreshIntegrationIssue::InvalidSyncStatusPlan,
                input.sync.statusPlan.error.empty()
                    ? "lite syncstatus plan is invalid"
                    : input.sync.statusPlan.error);
        }
        if (options.rejectExecutableSyncPlans && input.sync.statusPlan.bridgeExecutionAllowed) {
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::UnsafePlan,
                LiteWalletSyncAppRefreshIntegrationIssue::SyncStatusWouldExecute,
                "lite syncstatus plan would allow bridge execution");
        }
        result.syncStatusPlanAccepted = true;
    }

    if (options.requireRecoveryDecision && !input.sync.haveRecoveryDecision) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncStatus,
            LiteWalletSyncAppRefreshIntegrationIssue::MissingRecoveryDecision,
            "lite sync recovery decision is required before app refresh integration");
    }

    if (input.sync.haveRecoveryDecision) {
        const auto& decision = input.sync.recoveryDecision;
        if (decision.kind == LiteSyncRecoveryDecisionKind::InvalidStatus) {
            result.recoveryRequired = true;
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::WaitingForRecovery,
                LiteWalletSyncAppRefreshIntegrationIssue::SyncRecoveryNeedsUserAttention,
                decision.reason.empty()
                    ? "lite sync status is invalid and needs user attention"
                    : decision.reason);
        }
        if (decision.kind == LiteSyncRecoveryDecisionKind::Stuck ||
            decision.kind == LiteSyncRecoveryDecisionKind::ReorgDetected) {
            result.recoveryRequired = true;
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::WaitingForRecovery,
                LiteWalletSyncAppRefreshIntegrationIssue::SyncRecoveryRequired,
                decision.reason.empty()
                    ? "lite sync recovery must be modeled before refresh can feed a queue"
                    : decision.reason);
        }
        if (options.requireSyncCompleteForRefresh && decision.kind == LiteSyncRecoveryDecisionKind::KeepPolling) {
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::WaitingForSyncStatus,
                LiteWalletSyncAppRefreshIntegrationIssue::SyncStillPolling,
                decision.reason.empty()
                    ? "lite sync is still polling"
                    : decision.reason);
        }
        result.recoveryAccepted = true;
    }

    if (input.cancellation.cancellationRequested && input.cancellation.syncInProgress) {
        result.cancellationRequired = true;
        if (!input.cancellation.cancellationSupported) {
            return stoppedResult(
                std::move(result),
                LiteWalletSyncAppRefreshIntegrationStatus::WaitingForCancellation,
                LiteWalletSyncAppRefreshIntegrationIssue::CancellationUnsupported,
                "lite sync cancellation is requested but no cancellation path is available");
        }
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WaitingForCancellation,
            LiteWalletSyncAppRefreshIntegrationIssue::CancellationRequired,
            "lite sync cancellation must complete before app refresh can feed a queue");
    }
    result.cancellationAccepted = true;

    if (refreshReportIsUnsafe(input.refreshOrchestration)) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::UnsafePlan,
            LiteWalletSyncAppRefreshIntegrationIssue::RefreshReportUnsafe,
            "lite app refresh orchestration report is not a no-network dry-run report");
    }

    if (!input.refreshOrchestration.ok) {
        const auto issue = input.refreshOrchestration.blocked
            ? LiteWalletSyncAppRefreshIntegrationIssue::RefreshBlocked
            : LiteWalletSyncAppRefreshIntegrationIssue::RefreshReportRejected;
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::RefreshNotQueued,
            issue,
            input.refreshOrchestration.error.empty()
                ? "lite app refresh orchestration report is not queued"
                : input.refreshOrchestration.error);
    }

    result.refreshReportAccepted = true;

    if (input.refreshOrchestration.skipped || !input.refreshOrchestration.wouldQueueRefresh) {
        return skippedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationIssue::RefreshSkipped,
            input.refreshOrchestration.issues.empty()
                ? "lite app refresh orchestration skipped queueing"
                : input.refreshOrchestration.issues.front().message);
    }

    if (options.requireFutureWorkerQueue && !input.futureWorkerQueue.enabled) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WorkerQueueUnavailable,
            LiteWalletSyncAppRefreshIntegrationIssue::FutureWorkerQueueDisabled,
            "future lite app refresh worker queue is disabled");
    }

    if (input.futureWorkerQueue.maxQueueDepth > 0 &&
        input.futureWorkerQueue.queueDepth >= input.futureWorkerQueue.maxQueueDepth) {
        return stoppedResult(
            std::move(result),
            LiteWalletSyncAppRefreshIntegrationStatus::WorkerQueueUnavailable,
            LiteWalletSyncAppRefreshIntegrationIssue::FutureWorkerQueuePressure,
            "future lite app refresh worker queue is at capacity");
    }

    result.ok = true;
    result.status = LiteWalletSyncAppRefreshIntegrationStatus::FutureWorkerQueueFeedReady;
    result.futureWorkerQueueFeedReady = true;
    result.wouldFeedFutureWorkerQueue = true;
    result.futureWorkerQueuePlan.readyToFeed = true;
    result.futureWorkerQueuePlan.wouldFeed = true;
    return result;
}

LiteWalletSyncAppRefreshIntegrationPlanner::LiteWalletSyncAppRefreshIntegrationPlanner(
    LiteWalletSyncAppRefreshIntegrationOptions options)
    : options_(options)
{
}

LiteWalletSyncAppRefreshIntegrationResult LiteWalletSyncAppRefreshIntegrationPlanner::plan(
    const LiteWalletSyncAppRefreshIntegrationInput& input) const
{
    return planLiteWalletSyncAppRefreshIntegration(input, options_);
}

} // namespace dragonx::wallet