{- |
Module      : Web.Api.WebDriver.Helpers
Description : Higher level WebDriver utilities.
Copyright   : 2018, Automattic, Inc.
License     : GPL-3
Maintainer  : Nathan Bloomfield (nbloomf@gmail.com)
Stability   : experimental
Portability : POSIX
-}

module Web.Api.WebDriver.Helpers (
  -- * Data
    writeDataFile
  , readDataFile
  , writeJsonFile
  , readJsonFile

  -- * Secrets
  , stashCookies
  , loadCookies

  -- * Actions
  , press
  , typeString
  ) where

import Control.Monad.Trans.Class
  ( MonadTrans(..) )
import qualified Data.Aeson as Aeson
  ( encode, ToJSON(..), Value )
import Data.ByteString.Lazy
  ( ByteString )
import qualified Data.ByteString.Lazy.Char8 as BS
  ( pack )
import qualified Data.Digest.Pure.SHA as SHA
  ( showDigest, sha1 )

import Web.Api.WebDriver.Endpoints
import Web.Api.WebDriver.Monad
import Web.Api.WebDriver.Types
import Web.Api.WebDriver.Types.Keyboard





-- | Save all cookies for the current domain to a file.
stashCookies
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => String -- ^ Passed through SHA1, and the digest is used as the filename.
  -> WebDriverTT t eff ()
stashCookies string =
  let file = SHA.showDigest $ SHA.sha1 $ BS.pack string in
  getAllCookies >>= writeCookieFile file


-- | Load cookies from a file saved with `stashCookies`. Returns `False` if the cookie file is missing or cannot be read.
loadCookies
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => String -- ^ Passed through SHA1, and the digest is used as the filename.
  -> WebDriverTT t eff Bool
loadCookies string = do
  let file = SHA.showDigest $ SHA.sha1 $ BS.pack string
  contents <- readCookieFile file
  case contents of
    Nothing -> return False
    Just cs -> do
      mapM_ addCookie cs
      return True


-- | Write cookies to a file under the secrets path. 
writeCookieFile
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => FilePath -- ^ File path; relative to @$DATA_PATH\/secrets\/cookies\/@
  -> [Cookie]
  -> WebDriverTT t eff ()
writeCookieFile file cookies = do
  path <- fromEnv (_dataPath . _env)
  let fullpath = path ++ "/secrets/cookies/" ++ file
  writeFilePath fullpath (Aeson.encode cookies)


-- | Read cookies from a file stored with `writeCookieFile`. Returns `Nothing` if the file does not exist.
readCookieFile
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => FilePath -- ^ File path; relative to @$DATA_PATH\/secrets\/cookies\/@
  -> WebDriverTT t eff (Maybe [Cookie])
readCookieFile file = do
  path <- fromEnv (_dataPath . _env)
  let fullpath = path ++ "/secrets/cookies/" ++ file
  cookieFileExists <- fileExists fullpath
  if cookieFileExists
    then readFilePath fullpath
      >>= parseJson
      >>= constructFromJson
      >>= mapM constructFromJson
      >>= (return . Just)
    else return Nothing



-- | Write a `ByteString` to the data directory
writeDataFile
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => FilePath -- ^ File path, relative to @$DATA_PATH@, with leading slash
  -> ByteString
  -> WebDriverTT t eff ()
writeDataFile file contents = do
  path <- fromEnv (_dataPath . _env)
  writeFilePath (path ++ file) contents

-- | Read a `ByteString` from the data directory
readDataFile
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => FilePath -- ^ File path, relative to @$DATA_PATH@, with leading slash
  -> WebDriverTT t eff ByteString
readDataFile file = do
  path <- fromEnv (_dataPath . _env)
  readFilePath $ path ++ file



-- | Write JSON to the data directory
writeJsonFile
  :: (Monad eff, Monad (t eff), MonadTrans t, Aeson.ToJSON a)
  => FilePath -- ^ File path, relative to @$DATA_PATH@, with leading slash
  -> a
  -> WebDriverTT t eff ()
writeJsonFile file a = do
  path <- fromEnv (_dataPath . _env)
  writeFilePath (path ++ file) (Aeson.encode $ Aeson.toJSON a)

-- | Read a JSON `Value` from the data directory
readJsonFile
  :: (Monad eff, Monad (t eff), MonadTrans t)
  => FilePath -- ^ File path, relative to @$DATA_PATH@, with leading slash
  -> WebDriverTT t eff Aeson.Value
readJsonFile file = do
  path <- fromEnv (_dataPath . _env)
  readFilePath (path ++ file) >>= parseJson



-- | `KeyDownAction` with the given `Char`.
keypress :: Char -> ActionItem
keypress x = emptyActionItem
  { _actionType = Just KeyDownAction
  , _actionValue = Just [x]
  }


-- | Simulate pressing a `Key`.
press :: Key -> Action
press key = emptyAction
  { _inputSourceType = Just KeyInputSource
  , _inputSourceId = Just "kbd"
  , _actionItems = [keypress (keyToChar key)]
  }


-- | Simulate typing some text.
typeString :: String -> Action
typeString x = emptyAction
  { _inputSourceType = Just KeyInputSource
  , _inputSourceId = Just "kbd"
  , _actionItems = map keypress x
  }