{-# LANGUAGE RecordWildCards    #-}
module Main where

import            Control.Monad.Except        (MonadError(..), runExceptT, withExceptT)
import            Control.Monad.IO.Class      (liftIO)
import            Data.Monoid                 ((<>))
import qualified  Data.Text                   as TS
import            Data.Yaml                   (ParseException)
import            Options.Applicative
import            System.Exit                 (exitWith, ExitCode(..))

import            Config
import            LuminescentDreams.AuthDB


data Options = Options { configPath :: FilePath
                       , cmdOptions :: CommandOptions
                       }
               deriving (Show)

data CommandOptions = AddUser { uname :: String
                              , pword :: String
                              , admin :: Bool
                              }
                    | DeleteUser { uname :: String }
                    | ListUsers
                    -- | GrantAdmin { uname :: String }
                    -- | RevokeAdmin { uname :: String }
                    deriving (Show)

data AppException = ConfigException ParseException
                  | AuthDBExc AuthException
                  deriving (Show)

main :: IO ()
main = do
  res <- runExceptT $ do
    cliOpts@Options{..} <- liftIO $ execParser opts
    liftIO $ print cliOpts
    config@Config{..} <- withExceptT ConfigException (loadConfig configPath)
    liftIO $ print config

    authdb <- connectAuthDB authDBPath >>= either (throwError . AuthDBExc) return

    case cmdOptions of
      AddUser{..} -> withExceptT AuthDBExc $ runAuthDBE authdb $
        setPassword (Username $ TS.pack uname) (Password $ TS.pack pword)
      DeleteUser{..} -> undefined
      ListUsers -> withExceptT AuthDBExc (runAuthDBE authdb listUsers >>= liftIO . print)
      -- GrantAdmin{..} -> undefined
      -- RevokeAdmin{..} -> undefined

  case res of
    Left err -> print err >> exitWith (ExitFailure 1)
    Right _ -> return ()

  where
    opts = info (helper <*> cliOptions) fullDesc


cliOptions :: Parser Options
cliOptions =
  Options <$> strOption (long "config" <> value "config.yml")
          <*> subparser (
                 command "add" (info addOptions (progDesc "add a user"))
              <> command "delete" (info deleteOptions (progDesc "delete a user"))
              <> command "list" (info listOptions (progDesc "list users")))
              -- <> command "grant-admin" (info grantAdminOptions (progDesc "set the admin flag on a user"))
              -- <> command "revoke-admin" (info revokeAdminOptions (progDesc "revoke the admin flag on a user")))

addOptions :: Parser CommandOptions
addOptions = AddUser <$> argument str (metavar "username")
                     <*> argument str (metavar "password")
                     <*> switch (long "admin" <> help "set the admin flag on the user")

deleteOptions :: Parser CommandOptions
deleteOptions = DeleteUser <$> argument str (metavar "username")

listOptions :: Parser CommandOptions
listOptions = pure ListUsers

-- grantAdminOptions :: Parser CommandOptions
-- grantAdminOptions = GrantAdmin <$> argument str (metavar "username")
--
-- revokeAdminOptions :: Parser CommandOptions
-- revokeAdminOptions = RevokeAdmin <$> argument str (metavar "username")
