{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TemplateHaskell #-}
module Network.Mail.Newsletter.Web.Dedup.InMem
 ( InMemoryDedup(..)
 , newInMemDedup
 , HasInMemoryDedup(..)
 , genericInMemRecentlySubscribed
 ) where

import           Control.Concurrent.STM
import           Control.Lens
import           Control.Monad.Reader
import           Data.HashPSQ (HashPSQ)
import qualified Data.HashPSQ as PSQ
import           Data.Time
import           Data.Text (Text)
import           Network.Mail.Mime

data InMemoryDedup
 = InMemoryDedup
   { _psqTimeout    :: NominalDiffTime
   , _psqSubscribes :: TVar (HashPSQ Text UTCTime ())
   }

makeClassy ''InMemoryDedup

newInMemDedup :: MonadIO m => NominalDiffTime -> m InMemoryDedup
newInMemDedup d = liftIO $ InMemoryDedup d <$> newTVarIO PSQ.empty

genericInMemRecentlySubscribed :: (HasInMemoryDedup d, MonadReader d m, MonadIO m)
                               => Address -> m Bool
genericInMemRecentlySubscribed (Address _ email) = do
  now <- liftIO getCurrentTime
  p <- view psqSubscribes
  d <- view psqTimeout
  liftIO . atomically . stateTVar p $ \s' -> do
    let (_, s) = PSQ.atMostView ((-d) `addUTCTime` now) s'
    case PSQ.lookup email s of
      Nothing -> (True, PSQ.insert email now () s)
      Just _  -> (False, s)