{-# LANGUAGE CPP, QuasiQuotes #-}
module Yesod.Helpers.Auth.OAuth
    ( authOAuth
    , oauthUrl
    , authTwitter
    , twitterUrl
    ) where
import Yesod
import Yesod.Helpers.Auth
import Network.OAuth.Consumer
import Network.OAuth.Http.Request hiding (replace)
import qualified Network.OAuth.Http.Request as OA
import Network.OAuth.Http.CurlHttpClient
import Data.Maybe
import Data.String

oauthUrl :: String -> AuthRoute
oauthUrl name = PluginR name ["forward"]

authOAuth :: YesodAuth m =>
             String -- ^ Service Name
          -> String -- ^ OAuth Parameter Name to use for identify
          -> String -- ^ Request URL
          -> String -- ^ Access Token URL
          -> String -- ^ Authorize URL
          -> String -- ^ Consumer Key
          -> String -- ^ Consumer Secret
          -> AuthPlugin m
authOAuth name ident reqUrl accUrl authUrl key sec = AuthPlugin name dispatch login
  where
    url = PluginR name []
    dispatch "GET" ["forward"] = do
        render <- getUrlRender
        tm <- getRouteToMaster
        let app = Application key sec (URL $ render $ tm url)
        tok <- runOAuthM (fromApplication app) $ do
          signRq2 HMACSHA1 Nothing (fromJust . parseURL $ reqUrl) >>= oauthRequest CurlClient
        let oaUrl = fromString $ concat
                    [ authUrl
                    , "?oauth_token="
                    , findWithDefault ("oauth_token", "ERR") $ oauthParams tok
                    ]
        redirectString RedirectTemporary oaUrl
    dispatch "GET" [] = do
        render <- getUrlRender
        tm <- getRouteToMaster
        let app = Application key sec (URL $ render $ tm url)
        verifier <- runFormGet' $ stringInput "oauth_verifier"
        oaTok    <- runFormGet' $ stringInput "oauth_token"
        let appTok = fromApplication app
            params = OA.replace ("oauth_token", oaTok) $ oauthParams appTok
            reqTok = injectOAuthVerifier verifier $ ReqToken app params
        accTok <- runOAuthM reqTok $ do
          signRq2 HMACSHA1 Nothing (fromJust . parseURL $ accUrl) >>= oauthRequest CurlClient
        let crId = findWithDefault (ident, error "oops!") $ oauthParams accTok
            creds = Creds name crId $ toList $ oauthParams accTok
        setCreds True creds
    dispatch _ _ = notFound
    login tm = do
        render <- lift getUrlRender
        let oaUrl = render $ tm $ oauthUrl name
        addHtml
#if GHC7
          [hamlet|
#else
          [$hamlet|
#endif
             <a>!href=$oaUrl$ Login with $name$
          |]

authTwitter :: YesodAuth m =>
               String -- ^ Consumer Key
            -> String -- ^ Consumer Secret
            -> AuthPlugin m
authTwitter = authOAuth "twitter"
                        "screen_name"
                        "http://twitter.com/oauth/request_token"
                        "http://twitter.com/oauth/access_token"
                        "http://twitter.com/oauth/authorize"

twitterUrl :: AuthRoute
twitterUrl = oauthUrl "twitter"