{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}

module Claims.Interpreter
  ( eval
  , validateClaim
  ) where

import Claims.Types

-- | Evaluate a rule against a claim
eval :: Claim -> Rule a -> a
eval :: forall a. Claim -> Rule a -> a
eval Claim
claim Rule a
rule = case Rule a
rule of
  AmountGreaterThan Decimal
amt -> Claim -> Decimal
totalAmount Claim
claim Decimal -> Decimal -> Bool
forall a. Ord a => a -> a -> Bool
> Decimal
amt
  AmountLessThan Decimal
amt -> Claim -> Decimal
totalAmount Claim
claim Decimal -> Decimal -> Bool
forall a. Ord a => a -> a -> Bool
< Decimal
amt
  AmountBetween Decimal
low Decimal
high -> Claim -> Decimal
totalAmount Claim
claim Decimal -> Decimal -> Bool
forall a. Ord a => a -> a -> Bool
>= Decimal
low Bool -> Bool -> Bool
&& Claim -> Decimal
totalAmount Claim
claim Decimal -> Decimal -> Bool
forall a. Ord a => a -> a -> Bool
<= Decimal
high
  
  HasDiagnosisCode Text
code -> Text
code Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Claim -> [Text]
diagnosisCodes Claim
claim
  HasProcedureCode Text
code -> Text
code Text -> [Text] -> Bool
forall a. Eq a => a -> [a] -> Bool
forall (t :: * -> *) a. (Foldable t, Eq a) => a -> t a -> Bool
`elem` Claim -> [Text]
procedureCodes Claim
claim
  
  IsClaimType ClaimType
ct -> Claim -> ClaimType
claimType Claim
claim ClaimType -> ClaimType -> Bool
forall a. Eq a => a -> a -> Bool
== ClaimType
ct
  PlaceOfServiceIs Text
p -> Claim -> Text
placeOfService Claim
claim Text -> Text -> Bool
forall a. Eq a => a -> a -> Bool
== Text
p
  
  And Rule Bool
r1 Rule Bool
r2 -> Claim -> Rule Bool -> Bool
forall a. Claim -> Rule a -> a
eval Claim
claim Rule Bool
r1 Bool -> Bool -> Bool
&& Claim -> Rule Bool -> Bool
forall a. Claim -> Rule a -> a
eval Claim
claim Rule Bool
r2
  Or Rule Bool
r1 Rule Bool
r2 -> Claim -> Rule Bool -> Bool
forall a. Claim -> Rule a -> a
eval Claim
claim Rule Bool
r1 Bool -> Bool -> Bool
|| Claim -> Rule Bool -> Bool
forall a. Claim -> Rule a -> a
eval Claim
claim Rule Bool
r2
  Not Rule Bool
r -> Bool -> Bool
not (Claim -> Rule Bool -> Bool
forall a. Claim -> Rule a -> a
eval Claim
claim Rule Bool
r)
  
  Reject Text
msg -> Text -> ValidationResult
Invalid Text
msg
  Rule a
Approve -> a
ValidationResult
Valid
  RequireReview Text
msg -> Text -> ValidationResult
Invalid (Text
"Review Required: " Text -> Text -> Text
forall a. Semigroup a => a -> a -> a
<> Text
msg)
  
  If Rule Bool
cond Rule ValidationResult
thenRule Rule ValidationResult
elseRule ->
    if Claim -> Rule Bool -> Bool
forall a. Claim -> Rule a -> a
eval Claim
claim Rule Bool
cond
      then Claim -> Rule a -> a
forall a. Claim -> Rule a -> a
eval Claim
claim Rule a
Rule ValidationResult
thenRule
      else Claim -> Rule a -> a
forall a. Claim -> Rule a -> a
eval Claim
claim Rule a
Rule ValidationResult
elseRule

-- | Validate a claim against a list of rules
validateClaim :: Claim -> [Rule ValidationResult] -> [ValidationResult]
validateClaim :: Claim -> [Rule ValidationResult] -> [ValidationResult]
validateClaim Claim
claim [Rule ValidationResult]
rules = (Rule ValidationResult -> ValidationResult)
-> [Rule ValidationResult] -> [ValidationResult]
forall a b. (a -> b) -> [a] -> [b]
map (Claim -> Rule ValidationResult -> ValidationResult
forall a. Claim -> Rule a -> a
eval Claim
claim) [Rule ValidationResult]
rules