#include "wallet/lite_wallet_state_apply_executor.h"

#include <utility>

namespace dragonx::wallet {
namespace {

void addIssue(LiteWalletStateApplyExecutionResult& result,
              LiteWalletStateApplyExecutionIssue issue,
              std::string message)
{
    result.issues.push_back(LiteWalletStateApplyExecutionIssueInfo{issue, std::move(message)});
}

std::size_t countPlannedChanges(const LiteWalletStateApplyPlan& plan)
{
    std::size_t count = 0;
    for (const auto& fieldPlan : plan.fieldPlans) {
        if (fieldPlan.wouldChange) ++count;
    }
    for (const auto& collectionPlan : plan.collectionPlans) {
        if (collectionPlan.wouldReplace) ++count;
    }
    return count;
}

LiteWalletStateApplyExecutionResult baseResult(const LiteWalletStateApplyPlan& plan,
                                               LiteWalletStateApplyExecutorOptions options)
{
    LiteWalletStateApplyExecutionResult result;
    result.stateMutationRequested = options.requestStateMutation;
    result.stateMutationAllowed = false;
    result.applyImplemented = false;
    result.fieldPlanCount = plan.fieldPlans.size();
    result.collectionPlanCount = plan.collectionPlans.size();
    result.plannedChangeCount = countPlannedChanges(plan);
    result.planIssueCount = plan.issues.size();
    return result;
}

} // namespace

const char* liteWalletStateApplyExecutionStatusName(LiteWalletStateApplyExecutionStatus status)
{
    switch (status) {
        case LiteWalletStateApplyExecutionStatus::Disabled: return "Disabled";
        case LiteWalletStateApplyExecutionStatus::Rejected: return "Rejected";
        case LiteWalletStateApplyExecutionStatus::ImplementationMissing: return "ImplementationMissing";
    }
    return "Unknown";
}

const char* liteWalletStateApplyExecutionIssueName(LiteWalletStateApplyExecutionIssue issue)
{
    switch (issue) {
        case LiteWalletStateApplyExecutionIssue::InvalidPlan: return "InvalidPlan";
        case LiteWalletStateApplyExecutionIssue::MutablePlanRejected: return "MutablePlanRejected";
        case LiteWalletStateApplyExecutionIssue::StateMutationDisabled: return "StateMutationDisabled";
        case LiteWalletStateApplyExecutionIssue::StateMutationImplementationMissing: return "StateMutationImplementationMissing";
    }
    return "Unknown";
}

LiteWalletStateApplyExecutionResult executeLiteWalletStateApplyPlan(
    const LiteWalletStateApplyPlan& plan,
    LiteWalletStateApplyExecutorOptions options)
{
    auto result = baseResult(plan, options);

    if (!plan.ok) {
        result.status = LiteWalletStateApplyExecutionStatus::Rejected;
        addIssue(result,
                 LiteWalletStateApplyExecutionIssue::InvalidPlan,
                 "lite WalletState apply plan is not valid");
        result.error = plan.error.empty() ? result.issues.back().message : plan.error;
        return result;
    }

    if (!plan.dryRunOnly || plan.applyImplemented || plan.stateMutationAllowed) {
        result.status = LiteWalletStateApplyExecutionStatus::Rejected;
        addIssue(result,
                 LiteWalletStateApplyExecutionIssue::MutablePlanRejected,
                 "lite WalletState apply executor accepts dry-run-only plans only");
        result.error = result.issues.back().message;
        return result;
    }

    result.planAccepted = true;

    if (options.requestStateMutation) {
        result.status = LiteWalletStateApplyExecutionStatus::ImplementationMissing;
        addIssue(result,
                 LiteWalletStateApplyExecutionIssue::StateMutationImplementationMissing,
                 "real lite WalletState application requires a future explicit implementation");
        result.error = result.issues.back().message;
        return result;
    }

    result.ok = true;
    result.status = LiteWalletStateApplyExecutionStatus::Disabled;
    addIssue(result,
             LiteWalletStateApplyExecutionIssue::StateMutationDisabled,
             "lite WalletState application is disabled; dry-run plan was accepted for reporting only");
    return result;
}

LiteWalletStateApplyExecutor::LiteWalletStateApplyExecutor(LiteWalletStateApplyExecutorOptions options)
    : options_(options)
{
}

LiteWalletStateApplyExecutionResult LiteWalletStateApplyExecutor::execute(
    const LiteWalletStateApplyPlan& plan) const
{
    return executeLiteWalletStateApplyPlan(plan, options_);
}

} // namespace dragonx::wallet