{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}
module Buchhaltung.Commandline where

import           Buchhaltung.AQBanking
import           Buchhaltung.Add
import           Buchhaltung.Common
import           Buchhaltung.Importers
import           Buchhaltung.Match
import           Buchhaltung.OptionParsers
import           Control.Arrow
import           Control.DeepSeq
import           Control.Exception
import           Control.Monad.RWS.Strict
import           Control.Monad.Reader
import           Data.Maybe
import qualified Data.Text as T
import           Hledger.Data (Journal)
import           Hledger.Read (readJournalFile)
import           Options.Applicative
import           System.Directory
import           System.Environment
import           System.FilePath
import           System.Process
import           Text.Printf

runMain :: IO ()
runMain = do
  opts <- evaluate . force =<<
    customExecParser (prefs showHelpOnEmpty)
    =<< mainParser
    :: IO (RawOptions ())
  let
    prog ::  ErrorT IO ()
    prog = do
        config <- liftIO $ readConfigFromFile $ oProfile opts
        run (oAction opts) =<< toFull opts config
  either (error . T.unpack) return =<< runExceptT prog

-- * Running Option Parsers and Actions

run :: Action -> FullOptions () -> ErrorT IO ()
run (Add partners) options =
  void $ runRWST add options{oEnv = partners} mempty

run (Import version file action) options = runImport action
  where runImport (Paypal puser) =
          importReadWrite paypalImporter (options' puser) file
        runImport NatwestIntl =
          importReadWrite natwestIntlImporter (options' ()) file
        runImport BarclaysUk =
          importReadWrite barclaysUkImporter (options' ()) file
        runImport BarclaycardUs =
          importReadWrite barclaycardusImporter (options' ()) file
        runImport (ComdirectVisa blz) =
          importReadWrite comdirectVisaImporter (options' blz) file
        runImport (Pncbank accountId) =
          importReadWrite pncbankImporter (options' accountId) file
        runImport (Revolut user) =
          importReadWrite revolutImporter (options' user) file
        runImport (Monefy settings) =
          importReadWrite monefyImporter (options' settings) file
        runImport AQBankingImport =
          importReadWrite aqbankingImporter (options' ()) file
        options' env = options{oEnv = (env, version)}

run (Update version doMatch doRequest) options = do
  res <- runAQ options $ aqbankingListtrans doRequest
  void $ runRWST
    (mapM (importWrite $ iImport aqbankingImporter) res)
    options{oEnv = ((), version)} ()
  when doMatch $ run Match options

run (Commit hledger args) options = flip runReaderT options $ do
  un <- readUser $  show . name
  dir <- takeDirectory <&> absolute =<< readLedger mainLedger
  bal <- lift $ runAQ options $ readAqbanking ["listbal"]
  sheet <- lift $ run readProcess' ["balance", "-e", "tomorrow"] options
  liftIO $ do setCurrentDirectory dir
              callProcess "git" $ "commit":args ++
                ["-m", intercalateL "\n" $ ["User " ++ un, ""]
                  ++ bal ++ ["Balance Sheet:", sheet]]
  where run = if hledger then runHledger else runLedger

run ListBalances options = void $ runAQ options $ callAqbanking ["listbal"]
  
run Setup options = void $ runAQ options aqbankingSetup

run Match options =
  withSystemTempDirectory "dbacl" $ \tmpdir -> 
  loadJournal [Just . imported] options >>= match options{oEnv = tmpdir}

run (AQBanking args) options = void $ runAQ options $ callAqbanking args

run (Ledger args) options = runLedger callProcess args options

run (HLedger args) options = runHledger callProcess args options

runLedger run = runLedger' run cLedgerExecutable mainLedger
runHledger run = runLedger' run cHledgerExecutable (maybe mainLedger const =<< mainHledger)

runLedger' run getExec getLedger args options = flip runReaderT options $ do
  exec <- readConfig getExec
  ledger <- absolute =<< readLedger getLedger
  liftIO $ do
    setEnv "LEDGER" ledger
    run exec args