content: module Buchhaltung.OptionParsers where
  
  import           Buchhaltung.Common
  import           Control.Exception
  import           Control.Monad.RWS.Strict
  import           Data.Maybe
  import           Data.Foldable
  import qualified Data.Text as T
  import           Options.Applicative
  import           System.Directory
  import           System.Environment
  import           System.FilePath
  import qualified Text.PrettyPrint.ANSI.Leijen as D
  import           Text.Printf
  
  
  
  -- * Option parsers
  --
  -- https://hackage.haskell.org/package/optparse-applicative/docs/Options-Applicative-Builder.html
  
  mainParser = do
    env <- getEnvironment
    home <- try getHomeDirectory :: IO (Either SomeException FilePath)
    return $ info
      ( helper *> version *> (Options
                  <$> userP
                  <*> profile env home
                  <*> subparser commands
                  <*> pure ()
                  <*> pure ()
                )
      ) mempty
  
  paragraph :: String -> D.Doc
  paragraph = foldr ((D.</>) . D.text) mempty . words
  
  version = infoOption "buchhaltung, version 0.0.6" $ long "version" <> short 'v' <> help "Show version"
  
  userP :: Parser (Maybe Username)
  userP = optional $ (Username . T.pack) <&> strOption $ long "user"
          <> short 'u' <> metavar "USER"
          <> helpDoc
          (Just $ paragraph "Select the user. Default: first configured user")
  
  envVar = "BUCHHALTUNG"
  
  -- | optparse profile folder
  profile
    :: Exception b =>
       [(String, FilePath)] -> Either b FilePath -> Parser FilePath
  profile env home = strOption $
    long "profile"
    <> value (fromMaybe (either throw buch home) envBuch)
    <> short 'p'
    <> metavar "FOLDER"
    <>  helpDoc (Just $
         paragraph "path to the profile folder. Precedence (highest to lowerst):"
          D.<$> D.text "1. this command line option"
          D.<$> paragraph (printf "2. Environment option: \"%s\" %s" envVar $
                           (maybe "(not set)" (printf "= '%s'") envBuch :: String ))
          D.<$> paragraph (printf "3. %s %s" (buch "~") $
                            either (const "(home dir not available)")
                            (("= " ++) . buch) home)
                )
    where buch = (</> ".buchhaltung")
          envBuch = lookup envVar env
  
  versionP :: Parser (Maybe T.Text)
  versionP = optional . fmap T.pack
             $ strOption $ long "version"
              <> short 'v' <> metavar "VERSION"
  
  -- | optparse command parser
  -- commands :: Parser Action
  commands :: Mod CommandFields Action
  commands =
    command' "add"
    (Add . fmap (Username . T.pack) <$> many
      (strOption (short 'w' <> help "with partner USERNAME"
                 <> long "with"
                   <> metavar "USERNAME")))
    (progDesc "manual entry of new transactions")
  
    <> command' "import"
    (Import <$> versionP
      <*> strArgument (metavar "FILENAME")
      <*> subparser importOpts)
    (progDesc "import transactions from FILENAME")
  
    <> command' "update"
    (Update <$> versionP
      <*> (switch $ short 'm' <> long "match" <> help "run match after import")
      <*> (fmap not $ switch $ short 'n' <> long "no-fetch"
           <> help "do not fetch new transactions"))
    (progDesc
     "fetch and import AQ Banking transactions (using \"aqbanking request\" and \"listtrans\")")
  
    <> command' "match" (pure Match)
    (progDesc "manual entry of new transactions")
  
    <> command' "lb" (pure ListBalances)
    (progDesc "list balances of all AQBanking accounts (only available after 'update')")
  
    <> command' "setup" (pure Setup)
    (progDesc "initial setup of AQBanking")
  
    -- todo: pass through, that only sets env var and runs remaining args as "command args"
  
    <> passThrough (Commit False) "commit"  (Just "c") (commitMsg "ledger")
  
    <> passThrough (Commit True) "commitHledger"  (Just "ch") (commitMsg "hledger")
  
    <> passThrough Ledger "ledger" (Just "l") Nothing
  
    <> passThrough HLedger "hledger" (Just "hl") Nothing
  
    <> passThrough AQBanking "aqbanking" (Just "aq") Nothing
  
  commitMsg x = (Just $ concat
      ["run git commit in the dir of the user's mainLedger file and pass all "
      ,"following arguments to git. "
      ,"The Message will contain all AQBalances "
      ,"and the ", x ," balance sheet"])
    
  passThrough
    :: ([String] -> Action)
    -> String -- ^ command
    -> Maybe String -- ^ Short version
    -> Maybe String -- ^ Message
    -> Mod CommandFields Action
  passThrough constr long short desc = mconcat $ f <$> long : toList short
    where f cmdName = command' cmdName
            (constr <&> many $ strArgument mempty)
            $ noIntersperse
            <> (progDesc $ fromMaybe
                ("pass args through to '" ++ long ++ "'") desc)
  
  command' :: String -> Parser a -> InfoMod a -> Mod CommandFields a
  command' str parser infomod =
    command str $ info (helper *> parser) infomod
  
  importOpts :: Mod CommandFields ImportAction
  importOpts =
    metavar "FORMAT"
    <>
    commandGroup "Available FORMATS"
    <>
    command' "barclaycardus"
    (pure BarclaycardUs)
    (progDesc $ concat ["import from BarclaycardUs web export. "
                       ,"versions: May 2017"])
  
    <>
    command' "comdirectVisa"
    (ComdirectVisa . T.pack <$> strArgument
      (metavar "BLZ"))
    (progDesc $ concat ["import from german Comdirect Visa Statements. "
                       ,"versions: manuell, export"])
  
    <>
    command' "barclaysUk" (pure BarclaysUk)
    (progDesc $ concat ["import from Barclays UK CSV export. " ,"versions: 2017"])
    <>
    command' "natwestIntl" (pure NatwestIntl)
    (progDesc $ concat ["import from Natwest Internation CSV export. " ,"versions: 2017"])
    <>
    command' "monefy"
    (fmap Monefy $ MonefySettings . T.pack <$> strArgument
      (help "monefy instance or phone name (as configured in 'bankAccounts')"
        <> metavar "INSTANCE")
      <*> switch
      (short 's' <> help "Set this flag to use the following format for accounts: 'Monefy Category Account::<category>'"))
    (progDesc $ concat ["import from Monefy CSV export. "
                       ,"versions: 2017"])
    <>
    command' "revolut"
    (fmap Revolut $ RevolutSettings () . T.pack <$> strArgument
      (help "Revolut user (as configured in 'bankAccounts')"
        <> metavar "USER"))
    (progDesc $ concat ["manually constructed CSV from Revolut's PDF export."])
  
    <>
    command' "paypal"
    (Paypal . T.pack <$> strArgument
      (help "paypal username (as configured in 'bankAccounts')"
        <> metavar "PAYPAL_USERNAME"))
    (progDesc $ concat ["import from german Paypal CSV export with "
                       ,"\"alle guthaben relevanten Zahlungen "
                       ,"(kommagetrennt) ohne warenkorbdetails\". "
                       ,"versions: 2013, 2014, 2016, 2017"])
  
    <>
    command' "pncbank"
    (Pncbank . T.pack <$> strArgument
      (help "PNC account identifier or number (as configured in 'bankAccounts')"
        <> metavar "ACCOUNT_ID"))
    (progDesc $ concat ["import from PNC Bank web export. "
                       ,"versions: May 2017"])
  
    <> 
    command' "aqbanking"
    (pure AQBankingImport)
    (progDesc $ concat ["import CSV file generated by "
                       ,"\"aqbankingcli listtrans\". "
                       ,"versions: 4"])
  