{-|

The @accounts@ command lists account names:

- in flat mode (default), it lists the full names of accounts posted to by matched postings,
  clipped to the specified depth, possibly with leading components dropped.

- in tree mode, it shows the indented short names of accounts posted to by matched postings,
  and their parents, to the specified depth.

-}

{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE CPP #-}

module Hledger.Cli.Commands.Accounts (
  accountsmode
 ,accounts
 ,tests_Hledger_Cli_Commands_Accounts
) where

import Data.List
#if !(MIN_VERSION_base(4,11,0))
import Data.Monoid
#endif
-- import Data.Text (Text)
import qualified Data.Text as T
import System.Console.CmdArgs.Explicit as C
import Test.HUnit

import Hledger
import Prelude hiding (putStrLn)
import Hledger.Utils.UTF8IOCompat (putStrLn)
import Hledger.Cli.CliOptions


-- | Command line options for this command.
accountsmode = (defCommandMode $ ["accounts"] ++ aliases) {
  modeHelp = "show account names" `withAliases` aliases
 ,modeHelpSuffix = [
     "This command lists account names, either declared with account directives"
    ,"(--declared), posted to (--used), or both (default)."
    ,"With query arguments, only matched account names and account names" 
    ,"referenced by matched postings are shown."
    ,"It shows a flat list by default. With `--tree`, it uses indentation to"
    ,"show the account hierarchy."
    ,"In flat mode you can add `--drop N` to omit the first few account name components."
    ,"Account names can be depth-clipped with `--depth N` or depth:N."
   ]
 ,modeGroupFlags = C.Group {
     groupUnnamed = [
      flagNone ["declared"] (\opts -> setboolopt "declared" opts) "show account names declared with account directives"
     ,flagNone ["used"] (\opts -> setboolopt "used" opts) "show account names referenced by transactions"
     ,flagNone ["tree"] (\opts -> setboolopt "tree" opts) "show short account names, as a tree"
     ,flagNone ["flat"] (\opts -> setboolopt "flat" opts) "show full account names, as a list (default)"
     ,flagReq  ["drop"] (\s opts -> Right $ setopt "drop" s opts) "N" "flat mode: omit N leading account name parts"
     ]
    ,groupHidden = []
    ,groupNamed = [generalflagsgroup1]
    }
 }
  where aliases = ["a"]

-- | The accounts command.
accounts :: CliOpts -> Journal -> IO ()
accounts CliOpts{rawopts_=rawopts, reportopts_=ropts} j = do
  d <- getCurrentDay
  let q = queryFromOpts d ropts
      nodepthq = dbg1 "nodepthq" $ filterQuery (not . queryIsDepth) q
      depth    = dbg1 "depth" $ queryDepth $ filterQuery queryIsDepth q
      matcheddeclaredaccts = dbg1 "matcheddeclaredaccts" $ nub $ sort $ filter (matchesAccount q) $ map fst $ jaccounts j
      matchedps = dbg1 "ps" $ journalPostings $ filterJournalPostings nodepthq j
      matchedusedaccts = dbg1 "matchedusedaccts" $ nub $ sort $ filter (not . T.null) $ map (clipAccountName depth) $ map paccount matchedps
      used     = boolopt "used"   rawopts
      declared = boolopt "declared" rawopts
      as | declared     && not used = matcheddeclaredaccts
         | not declared && used     = matchedusedaccts
         | otherwise                = nub $ sort $ matcheddeclaredaccts ++ matchedusedaccts
      as' | tree_ ropts = expandAccountNames as
          | otherwise   = as
      render a | tree_ ropts = T.replicate (2 * (accountNameLevel a - 1)) " " <> accountLeafName a
               | otherwise   = maybeAccountNameDrop ropts a
  mapM_ (putStrLn . T.unpack . render) as'

tests_Hledger_Cli_Commands_Accounts = TestList []