{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE OverloadedStrings #-}

module Facebook.Object.User
  ( User(..)
  , Gender(..)
  , getUser
  , searchUsers
  , getUserCheckins
  , Friend(..)
  , getUserFriends
  ) where

import Control.Monad (mzero)
import Data.Aeson ((.:), (.:?))
import Data.Text (Text)
import Data.Typeable (Typeable)

import qualified Control.Monad.Trans.Resource as R
import qualified Data.Aeson as A

import Facebook.Types
import Facebook.Monad
import Facebook.Graph
import Facebook.Pager
import Facebook.Object.Checkin

-- | A Facebook user profile (see
-- <https://developers.facebook.com/docs/reference/api/user/>).
--
-- /NOTE:/ We still don't support all fields supported by
-- Facebook. Please fill an issue if you need access to any other
-- fields.
data User = User
  { userId :: UserId
  , userName :: Maybe Text
  , userFirstName :: Maybe Text
  , userMiddleName :: Maybe Text
  , userLastName :: Maybe Text
  , userGender :: Maybe Gender
  , userLocale :: Maybe Text
  , userUsername :: Maybe Text
  , userVerified :: Maybe Bool
  , userEmail :: Maybe Text
  , userLocation :: Maybe Place
  } deriving (Eq, Ord, Show, Read, Typeable)

instance A.FromJSON User where
  parseJSON (A.Object v) =
    User <$> v .: "id" <*> v .:? "name" <*> v .:? "first_name" <*>
    v .:? "middle_name" <*>
    v .:? "last_name" <*>
    v .:? "gender" <*>
    v .:? "locale" <*>
    v .:? "username" <*>
    v .:? "verified" <*>
    v .:? "email" <*>
    v .:? "location"
  parseJSON _ = mzero

-- | An user's gender.
data Gender
  = Male
  | Female
  deriving (Eq, Ord, Show, Read, Enum, Typeable)

instance A.FromJSON Gender where
  parseJSON (A.String "male") = return Male
  parseJSON (A.String "female") = return Female
  parseJSON _ = mzero

instance A.ToJSON Gender where
  toJSON = A.toJSON . toText
    where
      toText :: Gender -> Text
      toText Male = "male"
      toText Female = "female"

-- | Get an user using his user ID.  The user access token is
-- optional, but when provided more information can be returned
-- back by Facebook.  The user ID may be @\"me\"@, in which
-- case you must provide an user access token and information
-- about the token's owner is given.
getUser
  :: (R.MonadResource m, R.MonadUnliftIO m, R.MonadThrow m)
  => UserId -- ^ User ID or @\"me\"@.
  -> [Argument] -- ^ Arguments to be passed to Facebook.
  -> Maybe UserAccessToken -- ^ Optional user access token.
  -> FacebookT anyAuth m User
getUser id_ query mtoken = getObject ("/" <> idCode id_) query mtoken

-- | Search users by keyword.
searchUsers
  :: (R.MonadResource m, R.MonadUnliftIO m, R.MonadThrow m)
  => Text
  -> [Argument]
  -> Maybe UserAccessToken
  -> FacebookT anyAuth m (Pager User)
searchUsers = searchObjects "user"

-- | Get a list of check-ins made by a given user.
getUserCheckins
  :: (R.MonadResource m, R.MonadUnliftIO m, R.MonadThrow m)
  => UserId -- ^ User ID or @\"me\"@.
  -> [Argument] -- ^ Arguments to be passed to Facebook.
  -> UserAccessToken -- ^ User access token.
  -> FacebookT anyAuth m (Pager Checkin)
getUserCheckins id_ query token =
  getObject ("/" <> idCode id_ <> "/checkins") query (Just token)

-- | A friend connection of a 'User'.
data Friend = Friend
  { friendId :: UserId
  , friendName :: Text
  } deriving (Eq, Ord, Show, Read, Typeable)

instance A.FromJSON Friend where
  parseJSON (A.Object v) = Friend <$> v .: "id" <*> v .: "name"
  parseJSON _ = mzero

-- | Get the list of friends of the given user.
getUserFriends
  :: (R.MonadResource m, R.MonadUnliftIO m, R.MonadThrow m)
  => UserId -- ^ User ID or @\"me\"@.
  -> [Argument] -- ^ Arguments to be passed to Facebook.
  -> UserAccessToken -- ^ User access token.
  -> FacebookT anyAuth m (Pager Friend)
getUserFriends id_ query token =
  getObject ("/" <> idCode id_ <> "/friends") query (Just token)
