{-# LANGUAGE OverloadedStrings #-}
module Matterhorn.State.Users
  ( handleNewUsers
  , handleTypingUser
  , withFetchedUser
  , withFetchedUserMaybe
  )
where

import           Prelude ()
import           Matterhorn.Prelude

import qualified Data.Text as T
import qualified Data.Foldable as F
import qualified Data.Sequence as Seq
import           Data.Time ( getCurrentTime )
import           Lens.Micro.Platform

import qualified Network.Mattermost.Endpoints as MM
import           Network.Mattermost.Types

import           Matterhorn.Config
import           Matterhorn.Types
import           Matterhorn.State.Common


handleNewUsers :: Seq UserId -> MH () -> MH ()
handleNewUsers newUserIds after = do
    doAsyncMM Preempt getUserInfo addNewUsers
    where getUserInfo session =
              do nUsers <- MM.mmGetUsersByIds newUserIds session
                 let usrInfo u = userInfoFromUser u True
                     usrList = toList nUsers
                 return $ usrInfo <$> usrList

          addNewUsers :: [UserInfo] -> Maybe (MH ())
          addNewUsers is = Just $ mapM_ addNewUser is >> after

-- | Handle the typing events from the websocket to show the currently
-- typing users on UI
handleTypingUser :: UserId -> ChannelId -> MH ()
handleTypingUser uId cId = do
    config <- use (csResources.crConfiguration)
    when (configShowTypingIndicator config) $ do
        withFetchedUser (UserFetchById uId) $ const $ do
            ts <- liftIO getCurrentTime
            csChannels %= modifyChannelById cId (addChannelTypingUser uId ts)

-- | Given a user fetching strategy, locate the user in the state or
-- fetch it from the server, and pass the result to the specified
-- action. Assumes a single match is the only expected/valid result.
withFetchedUser :: UserFetch -> (UserInfo -> MH ()) -> MH ()
withFetchedUser fetch handle =
    withFetchedUserMaybe fetch $ \u -> do
        case u of
            Nothing -> postErrorMessage' "No such user"
            Just user -> handle user

withFetchedUserMaybe :: UserFetch -> (Maybe UserInfo -> MH ()) -> MH ()
withFetchedUserMaybe fetch handle = do
    st <- use id
    session <- getSession

    let localMatch = case fetch of
            UserFetchById uId -> userById uId st
            UserFetchByUsername uname -> userByUsername uname st
            UserFetchByNickname nick -> userByNickname nick st

    case localMatch of
        Just user -> handle $ Just user
        Nothing -> do
            mhLog LogGeneral $ T.pack $ "withFetchedUserMaybe: getting " <> show fetch
            doAsyncWith Normal $ do
                results <- case fetch of
                    UserFetchById uId ->
                        MM.mmGetUsersByIds (Seq.singleton uId) session
                    UserFetchByUsername uname ->
                        MM.mmGetUsersByUsernames (Seq.singleton $ trimUserSigil uname) session
                    UserFetchByNickname nick -> do
                        let req = UserSearch { userSearchTerm = trimUserSigil nick
                                             , userSearchAllowInactive = True
                                             , userSearchWithoutTeam = True
                                             , userSearchInChannelId = Nothing
                                             , userSearchNotInTeamId = Nothing
                                             , userSearchNotInChannelId = Nothing
                                             , userSearchTeamId = Nothing
                                             }
                        MM.mmSearchUsers req session

                return $ Just $ do
                    infos <- forM (F.toList results) $ \u -> do
                        let info = userInfoFromUser u True
                        addNewUser info
                        return info

                    case infos of
                        [match] -> handle $ Just match
                        [] -> handle Nothing
                        _ -> postErrorMessage' "Error: ambiguous user information"