{-# LANGUAGE ExistentialQuantification #-}
{-# LANGUAGE RecordWildCards           #-}

-- |
-- Module      : Advent.Throttle
-- Copyright   : (c) Justin Le 2018
-- License     : BSD3
--
-- Maintainer  : justin@jle.im
-- Stability   : experimental
-- Portability : non-portable
--
-- (Internal) Implement cacheing of API requests.

module Advent.Cache (
    cacheing
  , SaverLoader(..)
  , noCache
  ) where

import           Control.DeepSeq
import           Control.Exception
import           Control.Monad
import           Control.Monad.IO.Class
import           Data.Text              (Text)
import           System.Directory
import           System.FilePath
import           System.IO.Error
import qualified Data.Text.IO           as T

data SaverLoader a =
     SL { _slSave :: a -> Maybe Text
        , _slLoad :: Text -> Maybe a
        }

noCache :: SaverLoader a
noCache = SL (const Nothing) (const Nothing)

cacheing
    :: MonadIO m
    => FilePath
    -> SaverLoader a
    -> m a
    -> m a
cacheing fp SL{..} act = do
    old <- liftIO $ do
      createDirectoryIfMissing True (takeDirectory fp)
      (_slLoad =<<) <$> readFileMaybe fp
    case old of
      Nothing -> do
        r <- act
        liftIO . mapM_ (T.writeFile fp) $ _slSave r
        pure r
      Just o  -> pure o

readFileMaybe :: FilePath -> IO (Maybe Text)
readFileMaybe =
     (traverse (evaluate . force) . either (const Nothing) Just =<<)
   . tryJust (guard . isDoesNotExistError)
   . T.readFile