#pragma once

#include <xrpl/beast/utility/Journal.h>
#include <xrpl/ledger/View.h>
#include <xrpl/protocol/STTx.h>
#include <xrpl/tx/applySteps.h>

#include <utility>

namespace xrpl {

class HashRouter;
class ServiceRegistry;

/** Describes the pre-processing validity of a transaction.

    @see checkValidity, forceValidity
*/
enum class Validity {
    /// Signature is bad. Didn't do local checks.
    SigBad,
    /// Signature is good, but local checks fail.
    SigGoodOnly,
    /// Signature and local checks are good / passed.
    Valid
};

/** Checks transaction signature and local checks.

    @return A `Validity` enum representing how valid the
        `STTx` is and, if not `Valid`, a reason string.

    @note Results are cached internally, so tests will not be
        repeated over repeated calls, unless cache expires.

    @return `std::pair`, where `.first` is the status, and
            `.second` is the reason if appropriate.

    @see Validity
*/
std::pair<Validity, std::string>
checkValidity(HashRouter& router, STTx const& tx, Rules const& rules);

/** Sets the validity of a given transaction in the cache.

    @warning Use with extreme care.

    @note Can only raise the validity to a more valid state,
          and can not override anything cached bad.

    @see checkValidity, Validity
*/
void
forceValidity(HashRouter& router, uint256 const& txid, Validity validity);

/** Apply a transaction to an `OpenView`.

    This function is the canonical way to apply a transaction
    to a ledger. It rolls the validation and application
    steps into one function. To do the steps manually, the
    correct calling order is:
    @code{.cpp}
    preflight -> preclaim -> doApply
    @endcode
    The result of one function must be passed to the next.
    The `preflight` result can be safely cached and reused
    asynchronously, but `preclaim` and `doApply` must be called
    in the same thread and with the same view.

    @note Does not throw.

    For open ledgers, the `Transactor` will catch exceptions
    and return `tefEXCEPTION`. For closed ledgers, the
    `Transactor` will attempt to only charge a fee,
    and return `tecFAILED_PROCESSING`.

    If the `Transactor` gets an exception while trying
    to charge the fee, it will be caught and
    turned into `tefEXCEPTION`.

    For network health, a `Transactor` makes its
    best effort to at least charge a fee if the
    ledger is closed.

    @param app The current running `Application`.
    @param view The open ledger that the transaction
        will attempt to be applied to.
    @param tx The transaction to be checked.
    @param flags `ApplyFlags` describing processing options.
    @param journal A journal.

    @see preflight, preclaim, doApply

    @return A pair with the `TER` and a `bool` indicating
            whether or not the transaction was applied.
*/
ApplyResult
apply(
    ServiceRegistry& registry,
    OpenView& view,
    STTx const& tx,
    ApplyFlags flags,
    beast::Journal journal);

/** Enum class for return value from `applyTransaction`

    @see applyTransaction
*/
enum class ApplyTransactionResult {
    /// Applied to this ledger
    Success,
    /// Should not be retried in this ledger
    Fail,
    /// Should be retried in this ledger
    Retry
};

/** Transaction application helper

    Provides more detailed logging and decodes the
    correct behavior based on the `TER` type

    @see ApplyTransactionResult
*/
ApplyTransactionResult
applyTransaction(
    ServiceRegistry& registry,
    OpenView& view,
    STTx const& tx,
    bool retryAssured,
    ApplyFlags flags,
    beast::Journal journal);

}  // namespace xrpl
