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

import Web.Tweet
import Options.Applicative
import qualified Data.ByteString.Char8 as BS
import qualified Data.ByteString.Lazy.Char8 as BSL
import Control.Monad
import Data.Foldable (fold)
import Data.List hiding (delete)
import Data.Monoid hiding (getAll)
import System.Directory

-- | 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, replyHandles :: Maybe [String] }
    | Profile { count :: Maybe Int , screenName :: String }
    | Mentions { count :: Maybe Int }
    | Markov { screenName :: String }
    | Send { tweets :: Maybe Int , replyId :: Maybe String , replyHandles :: Maybe [String] , userInput :: String }
    | Sort { screenName :: String , count :: Maybe Int }
    | Delete { twId :: Integer }
    | Fav { twId :: Integer }
    | Unfav { twId :: Integer }
    | Retweet { twId :: Integer }
    | Unretweet { twId :: Integer }
    | Follow { screenName :: String }
    | Unfollow { screenName :: String }
    | Block { screenName :: String }
    | Unblock { 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 str = thread str [] 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 = execParser opts >>= select
    where
        opts = info (helper <*> 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 color) = case maybeFile of
    (Just file) -> selectCommand com color file
    _ -> selectCommand com color =<< (++ "/.cred") <$> getHomeDirectory

-- | Executes subcommand given subcommand + filepath to configuration file
selectCommand :: Command -> Bool -> FilePath -> IO ()
selectCommand (Send maybeNum Nothing Nothing input) _ file = fromCLI input (maybe 15 id maybeNum) file
selectCommand (Send maybeNum (Just replyId) Nothing input) _ file = thread input [] (pure . read $ replyId) (maybe 15 id maybeNum) file
selectCommand (Send maybeNum Nothing (Just handles) input) _ file = thread input handles Nothing (maybe 15 id maybeNum) file
selectCommand (SendInput maybeNum Nothing Nothing) _ file  = fromStdIn (maybe 15 id maybeNum) file
selectCommand (SendInput maybeNum (Just replyId) (Just handles)) _ file = threadStdIn handles (pure . read $ replyId) (maybe 15 id maybeNum) file
selectCommand (SendInput maybeNum (Just replyId) Nothing) _ file = threadStdIn [] (pure . read $ replyId) (maybe 15 id maybeNum) file
selectCommand (SendInput maybeNum Nothing (Just handles)) _ file = threadStdIn handles Nothing (maybe 15 id maybeNum) file
selectCommand (Timeline maybeNum) color file = putStrLn =<< showTimeline (maybe 11 id maybeNum) color file
selectCommand (Mentions maybeNum) color file = putStrLn =<< showTweets color <$> mentions (maybe 11 id maybeNum) file
selectCommand (Profile maybeNum name) color file = putStrLn =<< showProfile name (maybe 11 id maybeNum) color file
selectCommand (Sort name maybeNum) color file = putStrLn =<< showBest name (maybe 11 id maybeNum) color file
selectCommand (Markov name) _ file = do
    raw <- getMarkov name Nothing file
    writeFile (name ++ ".txt") (unlines raw)
    putStrLn $ "Written output to: " ++ name ++ ".txt"
selectCommand (Delete n) color file = do
    putStrLn "Deleted:\n"
    putStrLn =<< showTweets color <$> deleteTweetResponse n file
selectCommand (Fav n) color file = do
    putStrLn "Favorited:\n"
    putStrLn =<< showTweets color <$> favoriteTweetResponse n file
selectCommand (Unfav n) color file = do
    putStrLn "Unfavorited:\n"
    putStrLn =<< showTweets color <$> unfavoriteTweetResponse n file
selectCommand (Retweet n) color file = do
    putStrLn "Retweeted:\n"
    putStrLn =<< showTweets color <$> retweetTweetResponse n file
selectCommand (Unretweet n) color file = do
    putStrLn "Unretweeted:\n"
    putStrLn =<< showTweets color <$> unretweetTweetResponse n file
selectCommand (Follow screenName) _ file = do
    follow screenName file
    putStrLn ("..." ++ screenName ++ " followed successfully!")
selectCommand (Unfollow screenName) _ file = do
    unfollow screenName file
    putStrLn ("..." ++ screenName ++ " unfollowed successfully!")
selectCommand (Block screenName) color file = do
    block screenName file
    putStrLn ("..." ++ screenName ++ " blocked successfully")
selectCommand (Unblock screenName) color file = do
    unblock screenName file
    putStrLn ("..." ++ screenName ++ " unblocked successfully")
selectCommand (Dump screenName) color file = BSL.putStrLn =<< (getProfileRaw screenName 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 "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 "mention" (info mentionsParser (progDesc "Fetch mentions"))))
    <*> (optional $ strOption
        (long "cred"
        <> short 'c'
        <> metavar "CREDENTIALS"
        <> help "path to credentials"))
    <*> switch
        (long "color"
        <> short 'l'
        <> help "Display timeline with 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

-- | 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"))
    <*> argument str
        (metavar "SCREEN_NAME"
        <> help "Screen name of user you want to view.")

-- | 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"))

-- | 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 "Handles 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 "handles to include in replies")))