-- | Get the ReCaptcha result by querying Google's API.
--
-- This requires a "g-recaptcha-response" (POST) parameter to be set in the
-- current request.
--
-- See 'ReCaptchaResult' for possible failure types.
--
-- @
-- cstate <- getCaptcha
-- case cstate of
--   Success               -> writeText "Congratulations! You won."
--   Failure               -> writeText "Incorrect cstate answer."
--   MissingResponseParam  -> writeText "No g-recaptcha-response in POST"
--   InvalidServerResponse -> writeText "Did Google change their API?"
--   Errors errs           -> writeText ("Errors: " <> 'T.pack' ('show' errs))
-- @
--
-- This may throw a 'HTTP.HttpException' if there is a connection-related error.
getCaptcha :: HasReCaptcha b => Handler b c Captcha
getCaptcha = do
  mresponse <- getPostParam "g-recaptcha-response"
  case mresponse of
    Just answer -> withTop' captchaLens $ do
      manager  <- gets connectionManager
      getQuery <- gets recaptchaQuery
      remoteip <- getsRequest rqRemoteAddr
      response <- runReaderT (HTTP.httpLbs (getQuery remoteip answer)) manager
      -- The reply is a JSON object looking like
      -- {
      --   "success": true|false,
      --   "error-codes": [...]   // optional
      -- }
      -- see <https://developers.google.com/recaptcha/docs/verify>
      -- we just use aeson and the derived FromJSON instance here
      return $! case JSON.decode (HTTP.responseBody response) of
        Just obj
          | success      obj -> Success
          | invalidInput obj -> Failure
          | otherwise        -> Errors (fold (error_codes obj))
        Nothing -> InvalidServerResponse
    Nothing -> return MissingResponseParam
 where
  invalidInput obj = error_codes obj == Just ["invalid-input-response"]

-- | Run one of two handlers on either failing or succeeding a captcha.
--
-- @
-- 'withCaptcha' banForever $ do
--   postId <- 'getParam' "id"
--   thing  <- 'getPostParam' thing
--   addCommentToDB postId thing
-- @
--
-- See 'getCaptcha'
getCaptcha :: HasReCaptcha b => Handler b c Captcha
getCaptcha = do
  mresponse <- getPostParam "g-recaptcha-response"
  case mresponse of
    Just answer -> withTop' captchaLens $ do
      manager  <- gets connectionManager
      getQuery <- gets recaptchaQuery
      remoteip <- getsRequest rqRemoteAddr
      response <- runReaderT (HTTP.httpLbs (getQuery remoteip answer)) manager
      -- The reply is a JSON object looking like
      -- {
      --   "success": true|false,
      --   "error-codes": [...]   // optional
      -- }
      -- see <https://developers.google.com/recaptcha/docs/verify>
      -- we just use aeson and the derived FromJSON instance here
      return $! case JSON.decode (HTTP.responseBody response) of
        Just obj
          | success      obj -> Success
          | invalidInput obj -> Failure
          | otherwise        -> Errors (fold (error_codes obj))
        Nothing -> InvalidServerResponse
    Nothing -> return MissingResponseParam
 where
  invalidInput obj = error_codes obj == Just ["invalid-input-response"]

-- | Run one of two handlers on either failing or succeeding a captcha.
--
-- @
-- 'withCaptcha' banForever $ do
--   postId <- 'getParam' "id"
--   thing  <- 'getPostParam' thing
--   addCommentToDB postId thing
-- @
--
-- See 'getCaptcha'
getCaptcha = do
  mresponse <- getPostParam "g-recaptcha-response"
  case mresponse of
    Just answer -> withTop' captchaLens $ do
      manager  <- gets connectionManager
      getQuery <- gets recaptchaQuery
      remoteip <- getsRequest rqRemoteAddr
      response <- runReaderT (HTTP.httpLbs (getQuery remoteip answer)) manager
      -- The reply is a JSON object looking like
      -- {
      --   "success": true|false,
      --   "error-codes": [...]   // optional
      -- }
      -- see <https://developers.google.com/recaptcha/docs/verify>
      -- we just use aeson and the derived FromJSON instance here
      return $! case JSON.decode (HTTP.responseBody response) of
        Just obj
          | success      obj -> Success
          | invalidInput obj -> Failure
          | otherwise        -> Errors (fold (error_codes obj))
        Nothing -> InvalidServerResponse
    Nothing -> return MissingResponseParam
 where
  invalidInput obj = error_codes obj == Just ["invalid-input-response"]

-- | Run one of two handlers on either failing or succeeding a captcha.
--
-- @
-- 'withCaptcha' banForever $ do
--   postId <- 'getParam' "id"
--   thing  <- 'getPostParam' thing
--   addCommentToDB postId thing
-- @
--
-- See 'getCaptcha'