{-# LANGUAGE DeriveAnyClass        #-}
{-# LANGUAGE DeriveGeneric         #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings     #-}
{-# LANGUAGE TemplateHaskell       #-}
{-# LANGUAGE TypeSynonymInstances  #-}

module Monobank.Types where

import           Data.Aeson
import           Data.Aeson.TH
import           Data.Maybe
import qualified Data.Text      as T
import           Data.Time
import           GHC.Generics

import           Monobank.Utils

-----------------------------------------------------------------

-- | Wrapper for user token from Developer's Dashboard, https://api.monobank.ua/
type Token = T.Text

-----------------------------------------------------------------

data Currency =
  Currency
    { isoCode        :: String
    , isoNumericCode :: Int
    , decimalDigits  :: Int
    , symbol         :: String
    , name           :: String
    } deriving (Eq, Show)

convertCurrencyFromCode :: Int -> Currency
convertCurrencyFromCode val =
  case val of
    840       ->  Currency { isoCode = "USD", isoNumericCode=  840, decimalDigits= 2, symbol= "$", name="Dollar"}
    978       ->  Currency { isoCode = "EUR", isoNumericCode=  978, decimalDigits= 2, symbol= "€", name="Euro"}
    980       ->  Currency { isoCode = "UAH", isoNumericCode=  980, decimalDigits= 2, symbol= "₴", name="Hrivnya"}
    643       ->  Currency { isoCode = "RUB", isoNumericCode=  643, decimalDigits= 2, symbol= "", name="Ruble"}
    826       ->  Currency { isoCode = "GBP", isoNumericCode=  826, decimalDigits= 2, symbol= "", name="Pound Sterling"}
    949       ->  Currency { isoCode = "TRY", isoNumericCode=  949, decimalDigits= 2, symbol= "", name="Lira"}
    985       ->  Currency { isoCode = "PLN", isoNumericCode=  985, decimalDigits= 2, symbol= "", name="Zloty"}
    348       ->  Currency { isoCode = "HUF", isoNumericCode=  348, decimalDigits= 2, symbol= "", name="Forint"}
    208       ->  Currency { isoCode = "DKK", isoNumericCode=  208, decimalDigits= 2, symbol= "", name="Danish Krone"}
    203       ->  Currency { isoCode = "CZK", isoNumericCode=  203, decimalDigits= 2, symbol= "", name="Czech Koruna"}
    124       ->  Currency { isoCode = "CAD", isoNumericCode=  124, decimalDigits= 2, symbol= "", name="Canadian Dollar"}
    933       ->  Currency { isoCode = "BYN", isoNumericCode=  933, decimalDigits= 2, symbol= "", name="Belarussian Ruble"}
    756       ->  Currency { isoCode = "CHF", isoNumericCode=  756, decimalDigits= 2, symbol= "", name="Swiss Franc"}
    otherwise ->  Currency { isoCode = "NA" , isoNumericCode=  0  , decimalDigits= 2, symbol= "", name="Uknown"}

-------------------------------------------------------------------------------------------

-- | Data type that represents currency pair from Monobank
--   at spicific time
data CurrencyPair =
  CurrencyPair
    { cpCurrencyCodeA :: Currency      -- ^ Currency derived from international identificator
    , cpCurrencyCodeB :: Currency      -- ^ Currency derived from international identificator
    , cpDate          :: Int           -- ^ Timestamp for the currency pair information
    , cpRateSell      :: Maybe Float   -- ^ Rate to sell currency
    , cpRateBuy       :: Maybe Float   -- ^ Rate to buy currency
    , cpRateCross     :: Maybe Float   -- ^ Rate
    } deriving (Show)

instance FromJSON CurrencyPair where
  parseJSON =
    withObject "object" $ \o -> do
      currA'     <- o .:  "currencyCodeA"
      currB'     <- o .:  "currencyCodeB"
      date'      <- o .:  "date"         -- TODO: convert to UTCTime
      rateSell'  <- o .:? "rateSell"
      rateBuy'   <- o .:? "rateBuy"
      rateCross' <- o .:? "rateCross"

      let currA = convertCurrencyFromCode currA'
          currB = convertCurrencyFromCode currB'
          --date  =

      return $
        CurrencyPair currA currB date' rateSell' rateBuy' rateCross'

showCurrencyPair :: CurrencyPair -> IO ()
showCurrencyPair cp = do
  putStrLn $ (isoCode $ cpCurrencyCodeA cp) ++ "/" ++ (isoCode $ cpCurrencyCodeB cp)
  case (cpRateBuy cp) of
    Nothing  -> do
      putStrLn $ " - Cross: " ++ (show $ fromMaybe 0 $ cpRateCross cp) ++ (symbol $ cpCurrencyCodeB cp)
      putStrLn $ ""
    Just buy -> do
      putStrLn $ " - Buy:  " ++ (show $ buy) ++ " " ++ (symbol $ cpCurrencyCodeB cp)
      putStrLn $ " - Sell: " ++ (show $ fromMaybe 0 $ cpRateSell cp) ++ (symbol $ cpCurrencyCodeB cp)
      putStrLn $ ""

-- Simplified version for one-to-one conversion
--
-- instance FromJSON CurrencyPair where
--   parseJSON = genericParseJSON opts
--     where
--       opts = defaultOptions { fieldLabelModifier = uncapFst . drop 2}

--------------------------------------------------------------------------------

data Account =
  Account
    { acId           :: T.Text    -- ^
    , acBalance      :: Int       -- ^ Current balance
    , acCreditLimit  :: Int       -- ^ Current credit limit available to user
    , acCurrency     :: Currency  -- ^ Currency of the account
    , acCashbackType :: T.Text    -- ^
    } deriving (Eq, Show)

data User =
  User
    { uName     :: String    -- ^ User's full name
    , uAccounts :: [Account] -- ^ List of user accounts, by currency type
    } deriving (Eq, Show)

data Statement =
  Statement
    { stId              :: String   -- ^
    , stTime            :: String   -- ^
    , stDescription     :: String   -- ^
    , stMCC             :: String   -- ^
    , stHold            :: Bool     -- ^
    , stAmount          :: Int      -- ^
    , stOperationAmount :: Int      -- ^
    , stCurrency        :: Currency -- ^
    , stComissionRate   :: Int      -- ^
    , stCashbackAmount  :: Int      -- ^
    , balance           :: Int      -- ^
    } deriving (Eq, Show)