-- | Base64 encoding/decoding.

module Serokell.Util.Base64
       ( encode
       , decode
       , encodeUrl
       , decodeUrl
       , formatBase64
       , base64F
       , JsonByteString (..)
       , JsonByteStringDeprecated (..)
       ) where

import           Control.Monad              ((>=>))
import           Data.Aeson                 (FromJSON (parseJSON), ToJSON (toJSON))
import qualified Data.ByteString            as BS
import qualified Data.ByteString.Base64     as B64
import qualified Data.ByteString.Base64.URL as B64url
import           Data.Either.Combinators    (mapLeft)
import qualified Data.Text                  as T
import           Data.Text.Encoding         (decodeUtf8, encodeUtf8)
import           Data.Text.Lazy.Builder     (Builder, fromText)
import           Formatting                 (Format, later)

-- | Apply base64 encoding to strict ByteString.
encode :: BS.ByteString -> T.Text
encode = decodeUtf8 . B64.encode

-- | Decode base64-encoded ByteString.
decode :: T.Text -> Either T.Text BS.ByteString
decode = mapLeft T.pack . B64.decode . encodeUtf8

-- | Apply base64url encoding to strict ByteString.
encodeUrl :: BS.ByteString -> T.Text
encodeUrl = decodeUtf8 . B64url.encode

-- | Decode base64url-encoded ByteString.
decodeUrl :: T.Text -> Either T.Text BS.ByteString
decodeUrl = mapLeft T.pack . B64url.decode . encodeUtf8

-- | Construct Builder from bytestring formatting it in Base64.
formatBase64 :: BS.ByteString -> Builder
formatBase64 = fromText . encode

-- | Format which uses Base64 to print bytestring.
base64F :: Format r (BS.ByteString -> r)
base64F = later formatBase64

-- | Wrapper on top of ByteString with JSON serialization (in base64
-- encoding).
newtype JsonByteString = JsonByteString
    { getJsonByteString :: BS.ByteString
    }

instance ToJSON JsonByteString where
    toJSON = toJSON . encode . getJsonByteString

instance FromJSON JsonByteString where
    parseJSON =
        parseJSON >=>
        either (fail . T.unpack) (pure . JsonByteString) . decode

------------ Deprecated--------------
newtype JsonByteStringDeprecated = JsonByteStringDeprecated
    { getJsonByteStringDeprecated :: BS.ByteString
    }

instance ToJSON JsonByteStringDeprecated where
    toJSON = toJSON . encodeUrl . getJsonByteStringDeprecated

instance FromJSON JsonByteStringDeprecated where
    parseJSON =
        parseJSON >=>
        either (fail . T.unpack) (pure . JsonByteStringDeprecated) . decodeUrl