content: {-# LANGUAGE NamedFieldPuns #-}
  {-# LANGUAGE FlexibleContexts #-}
  {-# LANGUAGE OverloadedStrings #-}
  {-# OPTIONS_HADDOCK ignore-exports #-}
  module Buchhaltung.AQBanking where
  
  import           Buchhaltung.Common
  import           Control.Monad.RWS.Strict
  import           Data.Maybe
  import qualified Data.Text as T
  import           Formatting ((%))
  import qualified Formatting.ShortFormatters as F
  import           System.Directory
  import           System.FilePath
  import           System.Process as P
  
  -- * The Monad Stack and its runner
  
  type AQM = CommonM (AQConnection, AQBankingConf)
  
  -- | Runs an AQBanking Action for all connections of the selected user
  runAQ :: FullOptions () -> AQM a -> ErrorT IO [a]
  runAQ options action = fst <$> evalRWST action2 options ()
    where action2 = do
            user <- user
            aqconf <- maybeThrow ("AQBanking not configured for user "%F.sh)
                      ($ user) return $ aqBanking user
            forM (connections aqconf) $ \conn ->
              withRWST (\r s -> (r{oEnv = (conn,aqconf)}, s)) action
  
  -- * Direct access to the executables
  
  runProc ::
    (FilePath -> [String] -> IO a)
    -> [String] -> ([FilePath], FilePath)
    -> AQM a
  runProc run args (argsC, bin) = liftIO $ run bin $ argsC ++ args
  
  callAqhbci :: AAQM ()
  callAqhbci args = runProc callProcess args
              =<< askExec aqhbciToolExecutable "aqhbci-tool4" "-C"
  
  runAqbanking'
    :: (FilePath -> [String] -> IO b) -> AAQM b
  runAqbanking' prc args = do
    args' <- addContext args
    runProc prc args'
      =<< askExec aqBankingExecutable "aqbanking-cli" "-D"
  
  
  
  callAqbanking :: AAQM ()
  callAqbanking = runAqbanking' callProcess
  
  readAqbanking :: AAQM String
  readAqbanking = runAqbanking' $ readProcess'
  
  type AAQM a = [String] -> AQM a
  
  -- * Higher Level of Abstraction
  
  aqbankingListtrans :: Bool
                        -- ^ request new transactions
                     -> AQM T.Text
  aqbankingListtrans doRequest = do
    when doRequest $
      callAqbanking ["request"
                    , "--transactions"
                    , "--ignoreUnsupported"
                    ]
  
    T.pack <$> readAqbanking ["listtrans"
                             ]
  
  aqbankingSetup :: AQM ()
  aqbankingSetup = do
    path <- askConfigPath
    conn <- readConn return
    exists <- liftIO $ doesPathExist path
    when exists $ throwFormat
      ("Path '"%F.s%"' already exists. Cannot install into existing path.")
      ($ path)
    typ <- readConn $ return . aqType
    when (typ /= PinTan) $ throwError $ mconcat
      ["modes other than PinTan have to be setup manually. Refer to the "
      ,"AQBanking manual. Use the '-C' to point to the configured "
      ,"'configDir'."]
    liftIO $ createDirectoryIfMissing True path
    callAqhbci [ "adduser", "-t", "pintan", "--context=1"
              , "-b", aqBlz conn
              , "-u", aqUser conn
              , "-s", aqUrl conn
              , "-N", aqName conn
              , "--hbciversion=" <> toArg (aqHbciv conn)]
    callAqhbci [ "getsysid" ]
    callAqhbci [ "getaccounts" ]
    callAqhbci [ "listaccounts" ]
  
  -- * Utils
  
  addContext :: AAQM [FilePath]
  addContext [] = return []
  addContext args@(cmd:_) = do
    withC <- withContext cmd
    fmap (args ++) $
      if withC then (\x -> ["-c", x <.> "context"]) <$> askConfigPath
      else return []
  
  withContext "listbal"    = return True
  withContext "listtrans"  = return True
  withContext "request"    = return True
  withContext "listaccs"   = return False
  withContext cmd          = throwFormat
    ("'withContext' not defined for command '"%F.s%"'.")
    ($ cmd)
  
  askConfigPath :: AQM FilePath
  askConfigPath = do
    conn <- readConn return
    makeValid . (</> aqBlz conn <> "-" <> aqUser conn)
            <$> readConf (absolute . configDir)
  
  readConn :: (AQConnection -> AQM a) -> AQM a
  readConn f = f =<< reader (fst . oEnv)
  
  readConf :: (AQBankingConf -> AQM a) -> AQM a
  readConf f = f =<< reader (snd . oEnv)
  
  -- | Find out executable path and two args selecting the config file
  askExec
    :: (AQBankingConf -> Maybe FilePath)
    -> FilePath -- ^ default
    -> String -- ^ config path argument
    -> AQM ([FilePath], FilePath)
    -- ^ Path and Args
  askExec get def arg = do
    path <- askConfigPath
    readConf $ return . ((,) [arg, path]) . fromMaybe def . get
  