content: {-# LANGUAGE OverloadedStrings #-}
  {-# LANGUAGE TypeOperators     #-}
  
  module Cardano.Wallet.API.V1.Handlers.Transactions (
        handlers
      , newTransaction
      , getTransactionsHistory
      , estimateFees
      -- | Helper converter.
      , txFromMeta
      ) where
  
  import           Universum
  
  import           Servant
  
  import           Data.Coerce (coerce)
  
  import           Pos.Chain.Txp (TxId)
  import           Pos.Client.Txp.Util (defaultInputSelectionPolicy)
  import           Pos.Core (Address, Timestamp)
  
  import           Cardano.Wallet.API.Request
  import           Cardano.Wallet.API.Response
  import qualified Cardano.Wallet.API.V1.Transactions as Transactions
  import           Cardano.Wallet.API.V1.Types
  import           Cardano.Wallet.Kernel.CoinSelection.FromGeneric
                       (ExpenseRegulation (..))
  import           Cardano.Wallet.Kernel.DB.HdWallet (UnknownHdAccount)
  import           Cardano.Wallet.Kernel.DB.TxMeta (TxMeta)
  import qualified Cardano.Wallet.Kernel.Transactions as Kernel
  import           Cardano.Wallet.WalletLayer (ActiveWalletLayer,
                       NewPaymentError (..), PassiveWalletLayer)
  import qualified Cardano.Wallet.WalletLayer as WalletLayer
  import           Cardano.Wallet.WalletLayer.Kernel.Conv (toInputGrouping)
  
  handlers :: ActiveWalletLayer IO -> ServerT Transactions.API Handler
  handlers aw = newTransaction aw
           :<|> getTransactionsHistory (WalletLayer.walletPassiveLayer aw)
           :<|> estimateFees aw
           :<|> redeemAda aw
  
  -- | Given a 'Payment' as input, tries to generate a new 'Transaction', submitting
  -- it to the network eventually.
  newTransaction :: ActiveWalletLayer IO
                 -> Payment
                 -> Handler (APIResponse Transaction)
  newTransaction aw payment@Payment{..} = liftIO $ do
  
      -- NOTE(adn) The 'SenderPaysFee' option will become configurable as part
      -- of CBR-291.
      let inputGrouping = toInputGrouping $ fromMaybe (V1 defaultInputSelectionPolicy)
                                                      pmtGroupingPolicy
      res <- liftIO $ (WalletLayer.pay aw) (maybe mempty coerce pmtSpendingPassword)
                                           inputGrouping
                                           SenderPaysFee
                                           payment
      case res of
           Left err        -> throwM err
           Right (_, meta) -> txFromMeta aw NewPaymentUnknownAccountId meta
  
  txFromMeta :: Exception e
             => ActiveWalletLayer IO
             -> (UnknownHdAccount -> e)
             -> TxMeta
             -> IO (APIResponse Transaction)
  txFromMeta aw embedErr meta = do
      mTx <- WalletLayer.getTxFromMeta (WalletLayer.walletPassiveLayer aw) meta
      case mTx of
        Left err -> throwM (embedErr err)
        Right tx -> return $ single tx
  
  getTransactionsHistory :: PassiveWalletLayer IO
                         -> Maybe WalletId
                         -> Maybe AccountIndex
                         -> Maybe (V1 Address)
                         -> RequestParams
                         -> FilterOperations '[V1 TxId, V1 Timestamp] Transaction
                         -> SortOperations Transaction
                         -> Handler (APIResponse [Transaction])
  getTransactionsHistory pw mwalletId mAccIdx mAddr requestParams fops sops =
      liftIO $ do
          mRes <- WalletLayer.getTransactions pw mwalletId mAccIdx mAddr requestParams fops sops
          case mRes of
              Left err  -> throwM err
              Right res -> return res
  
  -- | Computes the fees generated by this payment, without actually sending
  -- the transaction to the network.
  estimateFees :: ActiveWalletLayer IO
               -> Payment
               -> Handler (APIResponse EstimatedFees)
  estimateFees aw payment@Payment{..} = do
      let inputGrouping = toInputGrouping $ fromMaybe (V1 defaultInputSelectionPolicy)
                                                      pmtGroupingPolicy
      res <- liftIO $ (WalletLayer.estimateFees aw) inputGrouping
                                                    SenderPaysFee
                                                    payment
      case res of
           Left err  -> throwM err
           Right fee -> return $ single (EstimatedFees (V1 fee))
  
  redeemAda :: ActiveWalletLayer IO
            -> Redemption
            -> Handler (APIResponse Transaction)
  redeemAda aw redemption = liftIO $ do
      res <- WalletLayer.redeemAda aw redemption
      case res of
        Left err        -> throwM err
        Right (_, meta) -> txFromMeta aw embedErr meta
    where
      embedErr :: UnknownHdAccount -> WalletLayer.RedeemAdaError
      embedErr = WalletLayer.RedeemAdaError . Kernel.RedeemAdaUnknownAccountId
  