{-# LANGUAGE DeriveDataTypeable, ScopedTypeVariables, OverloadedStrings, FlexibleContexts, FlexibleInstances, PatternGuards, ConstraintKinds #-}
-- | handle wallets
module Web.MangoPay.Wallets where


import Web.MangoPay.Monad
import Web.MangoPay.Types
import Web.MangoPay.Users

import Data.Text
import Data.Typeable (Typeable)
import Data.Aeson
import Data.Time.Clock.POSIX (POSIXTime)
import Control.Applicative
import qualified Data.HashMap.Lazy as HM (delete)

-- | create a wallet
createWallet ::  (MPUsableMonad m) => Wallet -> AccessToken -> MangoPayT m Wallet
createWallet = createGeneric "/wallets"


-- | modify a wallet
modifyWallet ::  (MPUsableMonad m) => Wallet -> AccessToken -> MangoPayT m Wallet
modifyWallet w = modifyGGeneric (Just $ HM.delete "Currency") "/wallets/" w wId


-- | fetch a wallet from its Id
fetchWallet :: (MPUsableMonad m) => WalletId -> AccessToken -> MangoPayT m Wallet
fetchWallet = fetchGeneric "/wallets/"

-- | list all wallets for a given user
listWallets :: (MPUsableMonad m) => AnyUserId -> Maybe Pagination -> AccessToken -> MangoPayT m (PagedList Wallet)
listWallets uid = genericList ["/users/",uid,"/wallets"]

-- | create a new fund transfer
createTransfer :: (MPUsableMonad m) => Transfer -> AccessToken -> MangoPayT m Transfer
createTransfer = createGeneric "/transfers"

-- | fetch a transfer from its Id
fetchTransfer :: (MPUsableMonad m) => TransferId -> AccessToken -> MangoPayT m Transfer
fetchTransfer = fetchGeneric "/transfers/"

-- | list transfers for a given wallet
listTransactions ::  (MPUsableMonad m) =>  WalletId  -> Maybe Pagination -> AccessToken -> MangoPayT m (PagedList Transaction)
listTransactions wid = genericList ["/wallets/",wid,"/transactions"]

-- | list transfer for a given user
listTransactionsForUser ::  (MPUsableMonad m) =>  AnyUserId  -> Maybe Pagination -> AccessToken -> MangoPayT m (PagedList Transaction)
listTransactionsForUser uid = genericList ["/users/",uid,"/transactions"]


-- | Id of a wallet
type WalletId=Text

-- | a wallet
data Wallet = Wallet {
        wId:: Maybe WalletId -- ^ The Id of the wallet
        ,wCreationDate :: Maybe POSIXTime -- ^ The creation date of the object
        ,wTag :: Maybe Text -- ^  Custom data
        ,wOwners :: [Text] -- ^ The owner of the wallet
        ,wDescription :: Text -- ^ A description of the wallet
        ,wCurrency :: Currency -- ^ Currency of the wallet
        ,wBalance :: Maybe Amount -- ^ The amount held on the wallet
        }
        deriving (Show,Eq,Ord,Typeable)

-- | to json as per MangoPay format
instance ToJSON Wallet where
        toJSON w=object ["Tag"  .= wTag w,"Owners" .= wOwners w,"Description" .= wDescription w,"Currency" .= wCurrency w]

-- | from json as per MangoPay format
instance FromJSON Wallet where
        parseJSON (Object v) =Wallet <$>
                         v .: "Id" <*>
                         v .: "CreationDate" <*>
                         v .:? "Tag" <*>
                         v .: "Owners" <*>
                         v .: "Description" <*>
                         v .: "Currency" <*>
                         v .: "Balance"
        parseJSON _=fail "Wallet"


-- | Id of a transfer
type TransferId=Text

-- | status of a transfer
data TransferStatus= Created | Succeeded | Failed
     deriving (Show,Read,Eq,Ord,Bounded,Enum,Typeable)

-- | to json as per MangoPay format
instance ToJSON TransferStatus  where
    toJSON Created="CREATED"
    toJSON Succeeded="SUCCEEDED"
    toJSON Failed="FAILED"

-- | from json as per MangoPay format
instance FromJSON TransferStatus where
    parseJSON (String "CREATED") =pure Created
    parseJSON (String "SUCCEEDED") =pure Succeeded
    parseJSON (String "FAILED") =pure Failed
    parseJSON _= fail "TransferStatus"

-- | transfer between wallets
data Transfer = Transfer{
        tId :: Maybe TransferId -- ^ Id of the transfer
        ,tCreationDate    :: Maybe POSIXTime -- ^  The creation date of the object
        ,tTag     :: Maybe Text -- ^   Custom data
        ,tAuthorId :: AnyUserId -- ^ The Id of the author
        ,tCreditedUserId  :: Maybe AnyUserId -- ^ The Id of the user owner of the credited wallet
        ,tDebitedFunds :: Amount -- ^ The funds debited from the « debited wallet »DebitedFunds – Fees = CreditedFunds (amount received on wallet)
        ,tFees  :: Amount -- ^  The fees taken on the transfer.DebitedFunds – Fees = CreditedFunds (amount received on wallet)
        ,tDebitedWalletId :: WalletId -- ^  The debited wallet (where the funds are held before the transfer)
        ,tCreditedWalletId:: WalletId -- ^ The credited wallet (where the funds will be held after the transfer)
        ,tCreditedFunds :: Maybe Amount -- ^  The funds credited on the « credited wallet »DebitedFunds – Fees = CreditedFunds (amount received on wallet)
        ,tStatus  :: Maybe TransferStatus -- ^   The status of the transfer:
        ,tResultCode      :: Maybe Text -- ^   The transaction result code
        ,tResultMessage   :: Maybe Text -- ^   The transaction result message
        ,tExecutionDate   :: Maybe POSIXTime -- ^  The execution date of the transfer
        }
        deriving (Show,Eq,Ord,Typeable)

-- | to json as per MangoPay format
instance ToJSON Transfer  where
    toJSON t=object ["AuthorId" .= tAuthorId t,"CreditedUserId" .= tCreditedUserId t,"DebitedFunds" .= tDebitedFunds t,
        "Fees" .= tFees t,"DebitedWalletId" .= tDebitedWalletId t,"CreditedWalletId" .= tCreditedWalletId t,
        "Tag" .= tTag t]

-- | from json as per MangoPay format
instance FromJSON Transfer where
        parseJSON (Object v) =Transfer <$>
                         v .: "Id" <*>
                         v .: "CreationDate" <*>
                         v .:? "Tag" <*>
                         v .: "AuthorId" <*>
                         v .: "CreditedUserId" <*>
                         v .: "DebitedFunds" <*>
                         v .: "Fees" <*>
                         v .: "DebitedWalletId" <*> -- yes, it's Id one way, Id the other
                         v .: "CreditedWalletId" <*> -- yes, it's Id one way, Id the other
                         v .:? "CreditedFunds" <*>
                         v .:? "Status" <*>
                         v .:? "ResultCode" <*>
                         v .:? "ResultMessage" <*>
                         v .:? "ExecutionDate"
        parseJSON _=fail "Transfer"

-- | type of transaction
data TransactionType = PAYIN
  | PAYOUT
  | TRANSFER
  deriving (Show,Read,Eq,Ord,Bounded,Enum,Typeable)

-- | to json as per MangoPay format
instance ToJSON TransactionType where
        toJSON =toJSON . show

-- | from json as per MangoPay format
instance FromJSON TransactionType where
        parseJSON (String s)
          | ((a,_):_)<-reads $ unpack s=pure a
        parseJSON _ =fail "TransactionType"

data TransactionNature =  REGULAR -- ^ just as you created the object
 | REFUND -- ^ the transaction has been refunded
 | REPUDIATION -- ^ the transaction has been repudiated
  deriving (Show,Read,Eq,Ord,Bounded,Enum,Typeable)

-- | to json as per MangoPay format
instance ToJSON TransactionNature where
        toJSON =toJSON . show

-- | from json as per MangoPay format
instance FromJSON TransactionNature where
        parseJSON (String s)
          | ((a,_):_)<-reads $ unpack s=pure a
        parseJSON _ =fail "TransactionNature"


type TransactionId = Text

-- | any transaction
data Transaction = Transaction{
        txId :: Maybe TransactionId -- ^ Id of the transfer
        ,txCreationDate    :: Maybe POSIXTime -- ^  The creation date of the object
        ,txTag     :: Maybe Text -- ^   Custom data
        ,txAuthorId :: AnyUserId -- ^ The Id of the author
        ,txCreditedUserId  :: Maybe AnyUserId -- ^ The Id of the user owner of the credited wallet
        ,txDebitedFunds :: Amount -- ^ The funds debited from the « debited wallet »DebitedFunds – Fees = CreditedFunds (amount received on wallet)
        ,txFees  :: Amount -- ^  The fees taken on the transfer.DebitedFunds – Fees = CreditedFunds (amount received on wallet)
        ,txDebitedWalletId :: Maybe WalletId -- ^  The debited wallet (where the funds are held before the transfer)
        ,txCreditedWalletId:: Maybe WalletId -- ^ The credited wallet (where the funds will be held after the transfer)
        ,txCreditedFunds :: Maybe Amount -- ^  The funds credited on the « credited wallet »DebitedFunds – Fees = CreditedFunds (amount received on wallet)
        ,txStatus  :: Maybe TransferStatus -- ^   The status of the transfer:
        ,txResultCode      :: Maybe Text -- ^   The transaction result code
        ,txResultMessage   :: Maybe Text -- ^   The transaction result message
        ,txExecutionDate   :: Maybe POSIXTime -- ^  The execution date of the transfer
        ,txType  :: TransactionType -- ^  The type of the transaction
        ,txNature  :: TransactionNature -- ^  The nature of the transaction:
        }
        deriving (Show,Eq,Ord,Typeable)

-- | to json as per MangoPay format
instance ToJSON Transaction  where
    toJSON t=object ["AuthorId" .= txAuthorId t,"CreditedUserId" .= txCreditedUserId t,"DebitedFunds" .= txDebitedFunds t,
        "Fees" .= txFees t,"DebitedWalletID" .= txDebitedWalletId t,"CreditedWalletID" .= txCreditedWalletId t,
        "Tag" .= txTag t,"Type" .= txType t,"Nature" .= txNature t]

-- | from json as per MangoPay format
instance FromJSON Transaction where
        parseJSON (Object v) =Transaction <$>
                         v .: "Id" <*>
                         v .: "CreationDate" <*>
                         v .:? "Tag" <*>
                         v .: "AuthorId" <*>
                         v .: "CreditedUserId" <*>
                         v .: "DebitedFunds" <*>
                         v .: "Fees" <*>
                         v .:? "DebitedWalletId" <*> -- yes, it's Id one way, Id the other
                         v .:? "CreditedWalletId" <*> -- yes, it's Id one way, Id the other
                         v .:? "CreditedFunds" <*>
                         v .:? "Status" <*>
                         v .:? "ResultCode" <*>
                         v .:? "ResultMessage" <*>
                         v .:? "ExecutionDate" <*>
                         v .: "Type"  <*>
                         v .: "Nature"
        parseJSON _=fail "Transfer"