-- | Provides IO action that parses command line options and tweetInputs from stdin
module Web.Tweet.Exec ( exec
                      , Program (..)
                      , Command (..)) where

import qualified Data.ByteString.Lazy.Char8 as BSL
import           Data.Maybe
import           Data.Monoid                hiding (getAll)
import           Data.Version
import           Options.Applicative
import           Paths_tweet_hs
import           System.Directory
import           Web.Tweet

-- | Data type for our program: one optional path to a credential file, (optionally) the number of tweetInputs to make, the id of the status you're replying to, and a list of users you wish to mention.
data Program = Program { subcommand :: Command , cred :: Maybe FilePath , color :: Bool }

-- | Data type for a command
-- TODO add boolean option to show ids alongside tweets
data Command = Timeline { count :: Maybe Int }
    | SendInput { tweetInputs :: Maybe Int, replyId :: Maybe String, replyh :: Maybe [String] }
    | Profile { count :: Maybe Int , screenName'' :: Maybe String, withReplies :: Bool, withRetweets :: Bool }
    | Mentions { count :: Maybe Int }
    | Markov { screenName' :: String }
    | Send { tweets :: Maybe Int , replyId :: Maybe String , replyh :: Maybe [String] , userInput :: String }
    | Sort { screenName' :: String , count :: Maybe Int , includeReplies :: Bool }
    | Delete { twId :: Integer }
    | Fav { twId :: Integer }
    | Unfav { twId :: Integer }
    | Retweet { twId :: Integer }
    | Unretweet { twId :: Integer }
    | List { count :: Maybe Int , screenName' :: String }
    | Follow { screenName' :: String }
    | Unfollow { screenName' :: String }
    | Block { screenName' :: String }
    | Unblock { screenName' :: String }
    | Mute { screenName' :: String }
    | Unmute { screenName' :: String }
    | Dump { screenName' :: String }

-- | query twitter to post stdin with no fancy options
fromStdIn :: Int -> FilePath -> IO ()
fromStdIn = threadStdIn [] Nothing

-- | Tweet string given to us, as parsed from the command line
fromCLI :: String -> Int -> FilePath -> IO ()
fromCLI s = thread s [] Nothing

-- | Threaded tweetInputs from stdIn
threadStdIn :: [String] -> Maybe Int -> Int -> FilePath -> IO ()
threadStdIn hs idNum num filepath = do
    contents <- getContents
    thread contents hs idNum num filepath

-- | Executes parser
exec :: IO ()
exec = putStrLn bird >> execParser opts >>= select
    where
        versionInfo = infoOption ("tweet-hs version: " ++ showVersion version) (short 'v' <> long "version" <> help "Show version")
        opts        = info (helper <*> versionInfo <*> program)
            (fullDesc
            <> progDesc "Tweet and view tweets"
            <> header "clit - a Command Line Interface Tweeter")

-- | Executes program given parsed `Program`
select :: Program -> IO ()
select (Program com maybeFile c) = case maybeFile of
    (Just file) -> selectCommand com (not c) file
    _ -> selectCommand com (not c) =<< (++ "/.cred.toml") <$> getHomeDirectory

-- | Executes subcommand given subcommand + filepath to configuration file
selectCommand :: Command -> Bool -> FilePath -> IO ()
selectCommand (Send maybeNum Nothing Nothing input) _ file = fromCLI input (fromMaybe 15 maybeNum) file
selectCommand (Send maybeNum (Just rId) Nothing input) _ file = thread input [] (pure . read $ rId) (fromMaybe 15 maybeNum) file
selectCommand (Send maybeNum Nothing (Just h) input) _ file = thread input h Nothing (fromMaybe 15 maybeNum) file
selectCommand (Send maybeNum (Just rId) (Just h) input) _ file = thread input h (pure . read $ rId) (fromMaybe 15 maybeNum) file
selectCommand (SendInput maybeNum Nothing Nothing) _ file  = fromStdIn (fromMaybe 15 maybeNum) file
selectCommand (SendInput maybeNum (Just rId) (Just h)) _ file = threadStdIn h (pure . read $ rId) (fromMaybe 15 maybeNum) file
selectCommand (SendInput maybeNum (Just rId) Nothing) _ file = threadStdIn [] (pure . read $ rId) (fromMaybe 15 maybeNum) file
selectCommand (SendInput maybeNum Nothing (Just h)) _ file = threadStdIn h Nothing (fromMaybe 15 maybeNum) file
selectCommand (Timeline maybeNum) c file = putStrLn =<< showTimeline (fromMaybe 11 maybeNum) c file
selectCommand (Mentions maybeNum) c file = putStrLn =<< showTweets c <$> mentions (fromMaybe 11 maybeNum) file
selectCommand (Profile maybeNum n False False) c file = putStrLn =<< showProfile (fromMaybe mempty n) (fromMaybe 11 maybeNum) c file
selectCommand (Profile maybeNum n True False) c file = putStrLn =<< showFilteredTL [filterReplies] (fromMaybe mempty n) (fromMaybe 11 maybeNum) c file
selectCommand (Profile maybeNum n False True) c file = putStrLn =<< showFilteredTL [filterRTs] (fromMaybe mempty n) (fromMaybe 11 maybeNum) c file
selectCommand (Profile maybeNum n True True) c file = putStrLn =<< showFilteredTL [filterReplies, filterRTs] (fromMaybe mempty n) (fromMaybe 11 maybeNum) c file
selectCommand (Sort n maybeNum False) c file = putStrLn =<< showBest' n (fromMaybe 11 maybeNum) c file
selectCommand (Sort n maybeNum True) c file = putStrLn =<< showBest n (fromMaybe 11 maybeNum) c file
selectCommand (List maybeNum n) c file = putStrLn =<< showFavorites (fromMaybe 11 maybeNum) n c file
selectCommand (Markov n) _ file = do
    raw <- getMarkov n Nothing file
    appendFile (n ++ ".txt") (unlines raw)
    putStrLn $ "Written output to: " ++ n ++ ".txt"
selectCommand (Delete n) c file = do
    putStrLn "Deleted:\n"
    putStrLn =<< showTweets c <$> deleteTweetResponse n file
selectCommand (Fav n) c file = do
    putStrLn "Favorited:\n"
    putStrLn =<< showTweets c <$> favoriteTweetResponse n file
selectCommand (Unfav n) c file = do
    putStrLn "Unfavorited:\n"
    putStrLn =<< showTweets c <$> unfavoriteTweetResponse n file
selectCommand (Retweet n) c file = do
    putStrLn "Retweeted:\n"
    putStrLn =<< showTweets c <$> retweetTweetResponse n file
selectCommand (Unretweet n) c file = do
    putStrLn "Unretweeted:\n"
    putStrLn =<< showTweets c <$> unretweetTweetResponse n file
selectCommand (Follow sn) _ file = do
    follow sn file
    putStrLn ("..." ++ sn ++ " followed successfully!")
selectCommand (Unfollow sn) _ file = do
    unfollow sn file
    putStrLn ("..." ++ sn ++ " unfollowed successfully!")
selectCommand (Block sn) _ file = do
    block sn file
    putStrLn ("..." ++ sn ++ " blocked successfully")
selectCommand (Unblock sn) _ file = do
    unblock sn file
    putStrLn ("..." ++ sn ++ " unblocked successfully")
selectCommand (Mute sn) _ file = do
    mute sn file
    putStrLn ("..." ++ sn ++ " muted successfully")
selectCommand (Unmute sn) _ file = do
    unmute sn file
    putStrLn ("..." ++ sn ++ " unmuted successfully")
selectCommand (Dump sn) _ file = BSL.putStrLn =<< getProfileRaw sn 3200 file Nothing

-- | Parser to return a program datatype
program :: Parser Program
program = Program
    <$> hsubparser
        (command "send" (info tweet (progDesc "Send a tweet from the command-line"))
        <> command "input" (info tweetInput (progDesc "Send a tweet from stdIn"))
        <> command "view" (info timeline (progDesc "Get your timeline"))
        <> command "user" (info profile (progDesc "Get a user's profile"))
        <> command "markov" (info markov (progDesc "Grab tweets en masse."))
        <> command "hits" (info best (progDesc "View a user's top tweets."))
        <> command "del" (info delete (progDesc "Delete a tweet.")) -- TODO delete/favorite in bunches!
        <> command "fav" (info fav (progDesc "Favorite a tweet"))
        <> command "ufav" (info unfav (progDesc "Unfavorite a tweet"))
        <> command "urt" (info unrt (progDesc "Un-retweet a tweet"))
        <> command "rt" (info rt (progDesc "Retweet a tweet"))
        <> command "follow" (info fol (progDesc "Follow a user"))
        <> command "unfollow" (info unfol (progDesc "Unfollow a user"))
        <> command "list" (info list (progDesc "List a user's favorites"))
        <> command "dump" (info dump (progDesc "Dump tweets (for debugging)"))
        <> command "block" (info blockParser (progDesc "Block a user"))
        <> command "unblock" (info unblockParser (progDesc "Unblock a user"))
        <> command "mute" (info muteParser (progDesc "Mute a user"))
        <> command "unmute" (info unmuteParser (progDesc "Unmute a user"))
        <> command "mentions" (info mentionsParser (progDesc "Fetch mentions")))
    <*> optional (strOption
        (long "cred"
        <> short 'c'
        <> metavar "CREDENTIALS"
        <> completer (bashCompleter "file -X '!*.toml' -o plusdirs")
        <> help "path to credentials"))
    <*> switch
        (long "color"
        <> short 'l'
        <> help "Turn off colorized terminal output.")

-- | Parser for the view subcommand
timeline :: Parser Command
timeline = Timeline
    <$> optional (read <$> strOption
        (long "count"
        <> short 'n'
        <> metavar "NUM"
        <> help "number of tweetInputs to fetch, default 5"))

-- | Parser for the markov subcommand
markov :: Parser Command
markov = Markov <$> user

-- | Parser for the follow subcommand
fol :: Parser Command
fol = Follow <$> user

-- | Parser for the block subcommand
blockParser :: Parser Command
blockParser = Block <$> user

-- | Parser for the unblock subcommand
unblockParser :: Parser Command
unblockParser = Unblock <$> user

-- | Parser for the dump subcommand
dump :: Parser Command
dump = Dump <$> user

-- | Parser for the unfollow subcommand
unfol :: Parser Command
unfol = Unfollow <$> user

-- | Parser for the list subcommand
list :: Parser Command
list = List
    <$> optional (read <$> strOption
        (long "count"
        <> short 'n'
        <> metavar "NUM"
        <> help "Number of tweetInputs to fetch, default 12"))
    <*> user

-- | Parser for the unfollow subcommand
muteParser :: Parser Command
muteParser = Mute <$> user

-- | Parser for the unfollow subcommand
unmuteParser :: Parser Command
unmuteParser = Unmute <$> user

-- | Parse a user screen name
user :: Parser String
user = argument str
    (metavar "SCREEN_NAME"
    <> help "Screen name of user.")

-- | Parser for the del subcommand
delete :: Parser Command
delete = Delete <$> getInt

-- | Parser for the fav subcommand
fav :: Parser Command
fav = Fav <$> getInt

-- | Parser for the fav subcommand
unfav :: Parser Command
unfav = Unfav <$> getInt

-- | Parser for the fav subcommand
unrt :: Parser Command
unrt = Unretweet <$> getInt

-- | Parser for the fav subcommand
rt :: Parser Command
rt = Retweet <$> getInt

-- | Parser for the del subcommand
getInt :: Parser Integer
getInt = read <$> argument str
    (metavar "TWEET_ID"
    <> help "ID of tweet")

-- | Parser for the user subcommand
profile :: Parser Command
profile = Profile
    <$> optional (read <$> strOption
        (long "count"
        <> short 'n'
        <> metavar "NUM"
        <> help "Number of tweetInputs to fetch, default 12"))
    <*> optional user
    <*> switch (
           long "no-replies"
        <> short 'r'
        <> help "Don't display replies.")
    <*> switch (
           long "no-retweets"
        <> short 't'
        <> help "Don't display retweets.")

-- | Parser for the mention subcommand
mentionsParser :: Parser Command
mentionsParser = Mentions
    <$> optional (read <$> strOption
        (long "count"
        <> short 'n'
        <> metavar "NUM"
        <> help "Number of tweetInputs to fetch, default 12"))

-- | Parse best tweets
best :: Parser Command
best = Sort
    <$> argument str
        (metavar "SCREEN_NAME"
        <> help "Screen name of user you want to view.")
    <*> optional (read <$> strOption
        (long "count"
        <> short 'n'
        <> metavar "NUM"
        <> help "Number of tweetInputs to fetch, default 12"))
    <*> switch
        (long "replies"
        <> short 'r'
        <> help "Include replies in your all-time hits")

-- | Parser for the send subcommand
tweet :: Parser Command
tweet = Send
    <$> optional (read <$> strOption
        (long "tweets"
        <> short 't'
        <> metavar "NUM"
        <> help "Number of tweetInputs in a row, default 15"))
    <*> optional (strOption
        (long "reply"
        <> short 'r'
        <> help "id of status to reply to - be sure to include their handle, e.g. @my_build_errors"))
    <*> optional (some $ strOption
        (long "handle"
        <> short 'h'
        <> metavar "HANDLE1"
        <> help "h to include in replies"))
    <*> (unwords <$> some (argument str
        (metavar "TEXT"
        <> help "text of tweet to be sent")))

-- | Parser for the input command
tweetInput :: Parser Command
tweetInput = SendInput
    <$> optional (read <$> strOption
        (long "tweets"
        <> short 't'
        <> metavar "NUM"
        <> help "Number of tweets in a row, default 15"))
    <*> optional (strOption
        (long "reply"
        <> short 'r'
        <> help "id of status to reply to - be sure to include their handle, e.g. @my_build_errors"))
    <*> optional (some $ argument str
        (metavar "HANDLE1"
        <> help "h to include in replies"))