{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell   #-}

module PayPal.Types.Payment where

import           Control.Applicative
import           Data.Aeson
import           Data.Aeson.TH
import           Data.Text
import           Data.Time
import           PayPal.Types.Util
import           PayPal.Util

--  HATEOAS Links
data Link = Link
            { linkHref   :: Text
            , linkRel    :: Text
            , linkMethod :: Text
            } deriving (Show, Eq)

--  Common Payments Objects

data PaymentIntent = PISale | PIAuthorize deriving (Show, Eq)
data PaymentState = PSCreated  | PSApproved | PSFailed | PSPending
                  | PSCanceled | PSExpired
                  deriving (Show, Eq)

data Payment = Payment
            { paymentIntent       :: PaymentIntent
            , paymentPayer        :: Payer
            , paymentTransactions :: [Transaction]
            , paymentRedirectUrls :: Maybe RedirectUrls
            , paymentId           :: Text
            , paymentCreateTime   :: UTCTime    -- Check serialize format
            , paymentState        :: PaymentState
            , paymentUpdateTime   :: UTCTime
            } deriving (Show, Eq)

data PaymentExecution = PaymentExecution
            { executionPayerId      :: Text
            , executionTransactions :: [Transaction]
            } deriving (Show, Eq)

data RedirectUrls = RedirectUrls
            { redirectReturnUrl :: Maybe Text
            , redirectCancelUrl :: Maybe Text
            } deriving (Show, Eq)

data FundingInstrument = FundingInstrument
            { fundingCreditCard      :: Maybe CreditCard
            , fundingCreditCardToken :: Maybe CreditCardToken
            } deriving (Show, Eq)

data CreditCardType = CCTVisa | CCTMastercard | CCTDiscover | CCTAmex
                      deriving (Show, Eq)

data CreditCardToken = CreditCardToken
            { tokenCreditCardId :: Text
            , tokenPayerId      :: Maybe Text
            , tokenLast4        :: Text
            , tokenType         :: CreditCardType
            , tokenExpireYear   :: Int
            , tokenExpireMonth  :: Int
            } deriving (Show, Eq)

data CreditCardState = CCSExpired | CCSOk deriving (Show, Eq)

data CreditCard = CreditCard
            { cardId             :: Maybe Text
            , cardPayerId        :: Maybe Text
            , cardNumber         :: Text
            , cardType           :: CreditCardType
            , cardExpireMonth    :: Int
            , cardExpireYear     :: Int
            , cardCvv2           :: Maybe Text
            , cardFirstName      :: Maybe Text
            , cardLastName       :: Maybe Text
            , cardBillingAddress :: Maybe Address
            , cardState          :: Maybe CreditCardState
            , cardValidUntil     :: Maybe Text
            } deriving (Show, Eq)

data PayerMethod = PMCreditCard | PMPayPal deriving (Show, Eq)

data Payer = Payer
            { payerPaymentMethod      :: PayerMethod
            , payerFundingInstruments :: [FundingInstrument]
            , payerPayerInfo          :: PayerInfo
            } deriving (Show, Eq)

data PayerInfo = PayerInfo
            { pinfoEmail           :: Maybe Text
            , pinfoFirstName       :: Maybe Text
            , pinfoLastName        :: Maybe Text
            , pinfoPayerId         :: Maybe Text
            , pinfoPhone           :: Maybe Text
            , pinfoShippingAddress :: ShippingAddress
            } deriving (Show, Eq)

data Address = Address
            { addressLine1       :: Text
            , addressLine2       :: Maybe Text
            , addressCity        :: Text
            , addressCountryCode :: Text
            , addressPostalCode  :: Maybe Text
            , addressState       :: Maybe Text
            , addressPhone       :: Maybe Text
            } deriving (Show, Eq)

data ShippingType = STResidential | STBusiness | STMailbox deriving (Show, Eq)
data ShippingAddress = ShippingAddress
            { shippingRecipientName :: Text
            , shippingType          :: ShippingType
            , shippingLine1         :: Text
            , shippingLine2         :: Maybe Text
            , shippingCity          :: Text
            , shippingCountryCode   :: Text
            , shippingPostalCode    :: Maybe Text
            , shippingState         :: Maybe Text
            , shippingPhone         :: Maybe Text
            } deriving (Show, Eq)

data Transaction = Transaction
            { transactionAmount           :: Amount
            , transactionDescription      :: Maybe Text
            , transactionItemList         :: Maybe ItemList
            , transactionRelatedResources :: [Resource]
            } deriving (Show, Eq)

data ItemList = ItemList
            { ilistItems           :: [Item]
            , ilistShippingAddress :: Maybe ShippingAddress
            } deriving (Show, Eq)

data Item = Item
            { itemQuantity :: Text
            , itemName     :: Text
            , itemPrice    :: Text
            , itemCurrency :: Text
            , itemSku      :: Maybe Text
            } deriving (Show, Eq)

data RefundState = RSPending | RSCompleted | RSFailed
                   deriving (Show, Eq)
data SaleState = SSPending | SSCompleted | SSRefunded | SSPartiallyRefunded
                 deriving (Show, Eq)
data AuthorizationState = ASPending | ASAuthorized | ASCaptured | ASPartiallyCaptured
                        | ASExpired | ASVoided
                        deriving (Show, Eq)
data CaptureState = CSPending | CSCompleted | CSRefunded | CSPartiallyRefunded
                    deriving (Show, Eq)

data Resource = Refund
                { refundId            :: Text
                , refundAmount        :: Amount
                , refundCreateTime    :: UTCTime
                , refundState         :: Maybe RefundState
                , refundSaleId        :: Maybe Text
                , refundParentPayment :: Maybe Text
                , refundUpdateTime    :: Maybe UTCTime
                }
              | Sale
                { saleId            :: Text
                , saleAmount        :: Amount
                , saleDescription   :: Maybe Text
                , saleCreateTime    :: Maybe UTCTime
                , saleState         :: Maybe SaleState
                , saleSaleId        :: Maybe Text
                , saleParentPayment :: Maybe Text
                , saleUpdateTime    :: Maybe UTCTime
                }
              | Authorization
                { authAmount        :: Amount
                , authCreateTime    :: Maybe UTCTime
                , authUpdateTime    :: Maybe UTCTime
                , authState         :: Maybe AuthorizationState
                , authParentPayment :: Maybe Text
                , authId            :: Maybe Text
                , authValidUntil    :: Maybe UTCTime
                , authLinks         :: [Link]
                }
              | Capture
                { captureAmount         :: Amount
                , captureIsFinalCapture :: Bool
                , captureCreateTime     :: Maybe UTCTime
                , captureUpdateTime     :: Maybe UTCTime
                , captureState          :: Maybe CaptureState
                , captureParentPayment  :: Maybe Text
                , captureId             :: Maybe Text
                , captureLinks          :: [Link]
                }
              deriving (Show, Eq)

data Amount = Amount
            { amountCurrency :: Text
            , amountTotal    :: Text
            , amountDetails  :: Maybe Details
            } deriving (Show, Eq)

data Details = Details
            { detailsShipping :: Maybe Text
            , detailsSubtotal :: Maybe Text
            , detailsTax      :: Maybe Text
            , detailsFee      :: Maybe Text
            } deriving (Show, Eq)

--  Instances
instance FromJSON (ListResponse Payment) where
    parseJSON (Object v) = ListResponse             <$>
                                v .:? "count"       <*>
                                v .:? "payments"    <*>
                                v .:? "next_id"
    parseJSON _          = Control.Applicative.empty

--  Aeson Instances
$(deriveJSON jsonUnderscore ''Link)
--
$(deriveJSON jsonUnderscore ''PaymentIntent)
$(deriveJSON jsonUnderscore ''PaymentState)
$(deriveJSON jsonUnderscore ''Payment)
$(deriveJSON jsonUnderscore ''PaymentExecution)
$(deriveJSON jsonUnderscore ''RedirectUrls)
$(deriveJSON jsonUnderscore ''FundingInstrument)
$(deriveJSON jsonUnderscore ''CreditCardState)
$(deriveJSON jsonUnderscore ''CreditCardType)
$(deriveJSON jsonUnderscore ''CreditCardToken)
$(deriveJSON jsonUnderscore ''CreditCard)
$(deriveJSON jsonUnderscore ''PayerMethod)
$(deriveJSON jsonUnderscore ''Payer)
$(deriveJSON jsonUnderscore ''PayerInfo)
$(deriveJSON jsonUnderscore ''Address)
$(deriveJSON jsonUnderscore ''ShippingType)
$(deriveJSON jsonUnderscore ''ShippingAddress)
$(deriveJSON jsonUnderscore ''Transaction)
$(deriveJSON jsonUnderscore ''ItemList)
$(deriveJSON jsonUnderscore ''Item)
$(deriveJSON jsonUnderscore ''RefundState)
$(deriveJSON jsonUnderscore ''SaleState)
$(deriveJSON jsonUnderscore ''AuthorizationState)
$(deriveJSON jsonUnderscore ''CaptureState)
$(deriveJSON jsonUnderscore ''Resource)
$(deriveJSON jsonUnderscore ''Amount)
$(deriveJSON jsonUnderscore ''Details)
