#pragma once

#include <xrpl/basics/base_uint.h>
#include <xrpl/ledger/ApplyView.h>
#include <xrpl/protocol/AccountID.h>
#include <xrpl/protocol/TER.h>
#include <xrpl/protocol/TxFlags.h>
#include <xrpl/protocol/nft.h>
#include <xrpl/tx/Transactor.h>

namespace xrpl {

namespace nft {

/** Delete up to a specified number of offers from the specified token offer
 * directory. */
std::size_t
removeTokenOffersWithLimit(
    ApplyView& view,
    Keylet const& directory,
    std::size_t maxDeletableOffers);

/** Returns tesSUCCESS if NFToken has few enough offers that it can be burned */
TER
notTooManyOffers(ReadView const& view, uint256 const& nftokenID);

/** Finds the specified token in the owner's token directory. */
std::optional<STObject>
findToken(ReadView const& view, AccountID const& owner, uint256 const& nftokenID);

/** Finds the token in the owner's token directory.  Returns token and page. */
struct TokenAndPage
{
    STObject token;
    std::shared_ptr<SLE> page;

    TokenAndPage(STObject const& token_, std::shared_ptr<SLE> page_)
        : token(token_), page(std::move(page_))
    {
    }
};
std::optional<TokenAndPage>
findTokenAndPage(ApplyView& view, AccountID const& owner, uint256 const& nftokenID);

/** Insert the token in the owner's token directory. */
TER
insertToken(ApplyView& view, AccountID owner, STObject&& nft);

/** Remove the token from the owner's token directory. */
TER
removeToken(ApplyView& view, AccountID const& owner, uint256 const& nftokenID);

TER
removeToken(
    ApplyView& view,
    AccountID const& owner,
    uint256 const& nftokenID,
    std::shared_ptr<SLE> const& page);

/** Deletes the given token offer.

    An offer is tracked in two separate places:
        - The token's 'buy' directory, if it's a buy offer; or
        - The token's 'sell' directory, if it's a sell offer; and
        - The owner directory of the account that placed the offer.

    The offer also consumes one incremental reserve.
 */
bool
deleteTokenOffer(ApplyView& view, std::shared_ptr<SLE> const& offer);

/** Repairs the links in an NFTokenPage directory.

    Returns true if a repair took place, otherwise false.
*/
bool
repairNFTokenDirectoryLinks(ApplyView& view, AccountID const& owner);

bool
compareTokens(uint256 const& a, uint256 const& b);

TER
changeTokenURI(
    ApplyView& view,
    AccountID const& owner,
    uint256 const& nftokenID,
    std::optional<xrpl::Slice> const& uri);

/** Preflight checks shared by NFTokenCreateOffer and NFTokenMint */
NotTEC
tokenOfferCreatePreflight(
    AccountID const& acctID,
    STAmount const& amount,
    std::optional<AccountID> const& dest,
    std::optional<std::uint32_t> const& expiration,
    std::uint16_t nftFlags,
    Rules const& rules,
    std::optional<AccountID> const& owner = std::nullopt,
    std::uint32_t txFlags = tfSellNFToken);

/** Preclaim checks shared by NFTokenCreateOffer and NFTokenMint */
TER
tokenOfferCreatePreclaim(
    ReadView const& view,
    AccountID const& acctID,
    AccountID const& nftIssuer,
    STAmount const& amount,
    std::optional<AccountID> const& dest,
    std::uint16_t nftFlags,
    std::uint16_t xferFee,
    beast::Journal j,
    std::optional<AccountID> const& owner = std::nullopt,
    std::uint32_t txFlags = tfSellNFToken);

/** doApply implementation shared by NFTokenCreateOffer and NFTokenMint */
TER
tokenOfferCreateApply(
    ApplyView& view,
    AccountID const& acctID,
    STAmount const& amount,
    std::optional<AccountID> const& dest,
    std::optional<std::uint32_t> const& expiration,
    SeqProxy seqProxy,
    uint256 const& nftokenID,
    XRPAmount const& priorBalance,
    beast::Journal j,
    std::uint32_t txFlags = tfSellNFToken);

TER
checkTrustlineAuthorized(
    ReadView const& view,
    AccountID const id,
    beast::Journal const j,
    Issue const& issue);

TER
checkTrustlineDeepFrozen(
    ReadView const& view,
    AccountID const id,
    beast::Journal const j,
    Issue const& issue);

}  // namespace nft

}  // namespace xrpl
