{-# LANGUAGE DeriveAnyClass #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE DerivingStrategies #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE QuasiQuotes #-}

module Utils.Gitlab
  ( showGitlabException,
    gitlabAPILimitErrorText,
    gitlabNotFoundErrorText,
  )
where

import Data.Aeson (FromJSON, ToJSON, decode)
import Data.ByteString.Char8 (ByteString)
import Data.ByteString.Lazy (fromStrict)
import Data.Text (Text, isPrefixOf)
import GHC.Generics (Generic)
import qualified Network.HTTP.Client as Client
import qualified Network.HTTP.Req as Req
import Network.HTTP.Types (Status (..))
import PyF (fmt)
import Utils.Req (showHTTPException, showRawResponse)

-- | Represents a typical Gitlab Error serialized as JSON like so:
--
-- @
-- {
--    "message": "the error reason"
-- }
-- @
newtype GitlabError
  = GitlabError
      { message :: Text
      }
  deriving stock (Generic, Show)
  deriving anyclass (FromJSON, ToJSON)

-- | Uses the helper to show generic HTTP issues and provides a specific handler for Gitlab
-- "business" exceptions
showGitlabException ::
  Req.HttpException ->
  Text
showGitlabException = showHTTPException handleGitlabException

handleGitlabException ::
  Client.Response () ->
  ByteString ->
  Text
handleGitlabException resp body =
  case statusCode (Client.responseStatus resp) of
    400 -> tryShowNiceErr
    404 -> gitlabNotFoundErrorText
    403 -> tryShowNiceErr
    _ -> showRawResponse resp body
  where
    err = decode $ fromStrict body :: Maybe GitlabError
    tryShowNiceErr = case err of
      Just gitlabError -> showRawGitlabMessage gitlabError
      Nothing -> showRawResponse resp body

showRawGitlabMessage ::
  GitlabError ->
  Text
showRawGitlabMessage err
  | isApiRateLimitError msg = gitlabAPILimitErrorText
  | otherwise = [fmt|From Gitlab: {msg}|]
  where
    msg = message err

gitlabNotFoundErrorText :: Text
gitlabNotFoundErrorText =
  [fmt|\
Could not find the indicated url.
It's possible that you have mistyped the URL
If not, this URL likely points to a private repository and you need to be authenticated to query its issues.
You might want to provide a gitlab API key with the --issuetracker-gitlabhost option.
See https://github.com/guibou/krank/blob/master/docs/Checkers/IssueTracker.md#gitlab|]

gitlabAPILimitErrorText :: Text
gitlabAPILimitErrorText =
  [fmt|\
Gitlab API Rate limit exceeded.
You might want to provide a gitlab API key with the --issuetracker-gitlabhost option.
See https://github.com/guibou/krank/blob/master/docs/Checkers/IssueTracker.md#gitlab|]

apiRateLimitPrefix :: Text
apiRateLimitPrefix = "API rate limit exceeded"

isApiRateLimitError ::
  Text ->
  Bool
isApiRateLimitError = isPrefixOf apiRateLimitPrefix