-- | Contains comment-related actions, like editing comments
--   and performing moderator actions on posts.
module Reddit.Actions.Comment
  ( getNewComments
  , getNewComments'
  , getMoreChildren
  , getCommentInfo
  , getCommentsInfo
  , editComment
  , deleteComment
  , removeComment ) where

import Reddit.Types.Comment
import Reddit.Types.Empty
import Reddit.Types.Error
import Reddit.Types.Listing
import Reddit.Types.Options
import Reddit.Types.Post
import Reddit.Types.Reddit
import Reddit.Types.Subreddit
import qualified Reddit.Routes as Route

import Data.Default.Class
import Data.Text (Text)
import Network.API.Builder (APIError(..))

-- | Get a 'CommentListing' for the most recent comments on the site overall.
--   This maps to <http://reddit.com/r/$SUBREDDIT/comments>, or <http://reddit.com/comments>
--   if the subreddit is not specified.
--   Note that none of the comments returned will have any child comments.
getNewComments :: Monad m => Maybe SubredditName -> RedditT m CommentListing
getNewComments = getNewComments' def

-- | Get a 'CommentListing' for the most recent comments with the specified 'Options' and
--   'SubredditName'. Note that none of the comments returned will have any child comments.
--   If the 'Options' is 'def', then this function is identical to 'getNewComments'.
getNewComments' :: Monad m => Options CommentID -> Maybe SubredditName -> RedditT m CommentListing
getNewComments' opts r = runRoute $ Route.newComments opts r

-- | Expand children comments that weren't fetched on initial load.
--   Equivalent to the web UI's "load more comments" button.
getMoreChildren :: Monad m
                => PostID -- ^ @PostID@ for the top-level
                -> [CommentID] -- ^ List of @CommentID@s to expand
                -> RedditT m [CommentReference]
getMoreChildren _ [] = return []
getMoreChildren p cs = do
  let (now, next) = splitAt 20 cs
  POSTWrapped rs <- runRoute $ Route.moreChildren p now
  more <- getMoreChildren p next
  return $ rs ++ more

-- | Given a 'CommentID', 'getCommentInfo' will return the full details for that comment.
getCommentInfo :: Monad m => CommentID -> RedditT m Comment
getCommentInfo c = do
  res <- getCommentsInfo [c]
  case res of
    Listing _ _ [comment] -> return comment
    _ -> failWith $ APIError InvalidResponseError

-- | Given a list of 'CommentID's, 'getCommentsInfo' will return another list containing
--   the full details for all the comments. Note that Reddit's
--   API imposes a limitation of 100 comments per request, so this function will fail immediately if given a list of more than 100 IDs.
getCommentsInfo :: Monad m => [CommentID] -> RedditT m CommentListing
getCommentsInfo cs =
  if null $ drop 100 cs
    then do
      res <- runRoute $ Route.commentsInfo cs
      case res of
        Listing _ _ comments | sameLength comments cs ->
          return res
        _ -> failWith $ APIError InvalidResponseError
    else failWith $ APIError TooManyRequests
  where
    sameLength (_:xs) (_:ys) = sameLength xs ys
    sameLength [] [] = True
    sameLength _ _ = False

-- | Edit a comment.
editComment :: Monad m
            => CommentID -- ^ Comment to edit
            -> Text -- ^ New comment text
            -> RedditT m Comment
editComment thing text = do
  POSTWrapped res <- runRoute $ Route.edit thing text
  return res

-- | Deletes one of your own comments. Note that this is different from
--   removing a comment as a moderator action.
deleteComment :: Monad m => CommentID -> RedditT m ()
deleteComment = nothing . runRoute . Route.delete

-- | Removes a comment (as a moderator action). Note that this is different
--   from deleting a comment.
removeComment :: Monad m => CommentID -> RedditT m ()
removeComment = nothing . runRoute . Route.removePost False