module Payer ( getExceedingPayers ) where import Data.Map (Map) import qualified Data.Map as M import Common.Model (ExceedingPayer (..), User (..), UserId) data Payer = Payer { _payer_userId :: UserId , _payer_preIncomePayments :: Int , _payer_postIncomePayments :: Int , _payer_income :: Int } data PostPaymentPayer = PostPaymentPayer { _postPaymentPayer_userId :: UserId , _postPaymentPayer_preIncomePayments :: Int , _postPaymentPayer_cumulativeIncome :: Int , _postPaymentPayer_ratio :: Float } getExceedingPayers :: [User] -> Map UserId Int -> Map UserId Int -> Map UserId Int -> [ExceedingPayer] getExceedingPayers users cumulativeIncome preIncomeRepartition postIncomeRepartition = let userIds = map _user_id users payers = getPayers userIds cumulativeIncome preIncomeRepartition postIncomeRepartition postPaymentPayers = map getPostPaymentPayer payers mbMaxRatio = safeMaximum . map _postPaymentPayer_ratio $ postPaymentPayers in case mbMaxRatio of Just maxRatio -> exceedingPayersFromAmounts . map (\p -> (_postPaymentPayer_userId p, getFinalDiff maxRatio p)) $ postPaymentPayers Nothing -> exceedingPayersFromAmounts . map (\p -> (_payer_userId p, _payer_preIncomePayments p)) $ payers getPayers :: [UserId] -> Map UserId Int -> Map UserId Int -> Map UserId Int -> [Payer] getPayers userIds cumulativeIncome preIncomeRepartition postIncomeRepartition = flip map userIds (\userId -> Payer { _payer_userId = userId , _payer_preIncomePayments = M.findWithDefault 0 userId preIncomeRepartition , _payer_postIncomePayments = M.findWithDefault 0 userId postIncomeRepartition , _payer_income = M.findWithDefault 0 userId cumulativeIncome } ) exceedingPayersFromAmounts :: [(UserId, Int)] -> [ExceedingPayer] exceedingPayersFromAmounts userAmounts = case mbMinAmount of Nothing -> [] Just minAmount -> filter (\payer -> _exceedingPayer_amount payer > 0) . map (\userAmount -> ExceedingPayer { _exceedingPayer_userId = fst userAmount , _exceedingPayer_amount = snd userAmount - minAmount } ) $ userAmounts where mbMinAmount = safeMinimum . map snd $ userAmounts getPostPaymentPayer :: Payer -> PostPaymentPayer getPostPaymentPayer payer = PostPaymentPayer { _postPaymentPayer_userId = _payer_userId payer , _postPaymentPayer_preIncomePayments = _payer_preIncomePayments payer , _postPaymentPayer_cumulativeIncome = _payer_income payer , _postPaymentPayer_ratio = (fromIntegral . _payer_postIncomePayments $ payer) / (fromIntegral $ _payer_income payer) } getFinalDiff :: Float -> PostPaymentPayer -> Int getFinalDiff maxRatio payer = let postIncomeDiff = truncate $ -1.0 * (maxRatio - _postPaymentPayer_ratio payer) * (fromIntegral . _postPaymentPayer_cumulativeIncome $ payer) in postIncomeDiff + _postPaymentPayer_preIncomePayments payer safeMinimum :: (Ord a) => [a] -> Maybe a safeMinimum [] = Nothing safeMinimum xs = Just . minimum $ xs safeMaximum :: (Ord a) => [a] -> Maybe a safeMaximum [] = Nothing safeMaximum xs = Just . maximum $ xs