{-# LANGUAGE OverloadedStrings, DeriveGeneric #-}

module Web.Twitter.Feed
  ( timeline
  , addLink
  , timelineUrl
  , sortLinks
  ) where

import qualified Data.ByteString.Lazy as BS

import Network.HTTP.Conduit
import Web.Authenticate.OAuth
import Data.Aeson
import Data.List (elemIndices, sort)
import Data.Char (toLower)
import Web.Twitter.Types

timeline :: OAuth -> Credential -> Int -> Bool -> String ->
            IO (Either String [SimpleTweet])
timeline oauth credential count excludeReplies username = do
  req <- createRequest username count excludeReplies
  res <- getResponse oauth credential req
  return $
    case decodeTweets res of
      Left message -> Left message
      Right ts     -> Right $ map (simplifyTweet . linkifyTweet) ts

createRequest :: String -> Int -> Bool -> IO Request
createRequest username count excludeReplies = parseUrl $ timelineUrl username count excludeReplies

getResponse :: OAuth -> Credential -> Request -> IO (Response BS.ByteString)
getResponse oauth credential req =
  withManager $ \m -> do
    signedreq <- signOAuth oauth credential req
    httpLbs signedreq m

decodeTweets :: Response BS.ByteString -> Either String [Tweet]
decodeTweets = eitherDecode . responseBody

timelineUrl :: String -> Int -> Bool -> String
timelineUrl user count excludeReplies =
    "https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name=" ++
    user ++ "&count=" ++ show count ++ "&exclude_replies=" ++
    (map toLower $ show excludeReplies)

linkifyTweet :: Tweet -> Tweet
linkifyTweet tweet = Tweet (processText (text tweet)
                                        (userEntities  $ entities tweet)
                                        (urlEntities   $ entities tweet)
                                        (mediaEntities $ entities tweet))
                           (createdAt tweet)
                           (idStr tweet)
                           (entities tweet)

processText :: String -> [UserEntity] -> [URLEntity] -> [MediaEntity] -> String
processText message users urls medias = foldr addLink message sortedLinks
  where sortedLinks = sortLinks urls users medias

sortLinks :: [URLEntity] -> [UserEntity] -> [MediaEntity] -> [Link]
sortLinks urls users medias = sort (map makeURLLink   urls ++
                                    map makeUserLink  users ++
                                    map makeMediaLink medias)

makeURLLink :: URLEntity -> Link
makeURLLink urlEntity = Link x y url
  where (x, y)  = urlIndices urlEntity
        urlText = displayUrl urlEntity
        href    = urlMessage urlEntity
        url     = "<a target=\"_blank\" href=\"" ++ href ++ "\">"
                    ++ urlText ++ "</a>"

makeMediaLink :: MediaEntity -> Link
makeMediaLink mediaEntity = Link x y url
  where (x, y)  = mediaIndices mediaEntity
        urlText = displayMediaUrl mediaEntity
        href    = mediaUrl mediaEntity
        url     = "<a target=\"_blank\" href=\"" ++ href ++ "\">"
                    ++ urlText ++ "</a>"

makeUserLink :: UserEntity -> Link
makeUserLink userEntity = Link x y mention
  where (x, y)   = userIndices userEntity
        username = screenName userEntity
        mention  = "<a target=\"_blank\" href=\"//twitter.com/" ++ username
                   ++ "\">@" ++ username ++ "</a>"

simplifyTweet :: Tweet -> SimpleTweet
simplifyTweet tweet =
    SimpleTweet { body = text tweet
                , tweetId = idStr tweet
                , created_at = createdAt tweet }

addLink :: Link -> String -> String
addLink (Link 139 140 link) tweet = before ++ " " ++ link ++ after
  where before = fst (splitAt (lastBlank tweet) tweet)
        after  = snd (splitAt 140 tweet)
        lastBlank = last . elemIndices ' '
addLink (Link start end link) tweet = before ++ link ++ after
  where before = fst (splitAt start tweet)
        after  = snd (splitAt end tweet)