content: {-# LANGUAGE TemplateHaskell #-}
  
  -- | Message embeds
  module Calamity.Types.Model.Channel.Embed (
    Embed (..),
    embedFooter,
    embedImage,
    embedThumbnail,
    embedAuthor,
    embedAuthor',
    embedField,
    EmbedFooter (..),
    EmbedImage (..),
    EmbedThumbnail (..),
    EmbedVideo (..),
    EmbedProvider (..),
    EmbedAuthor (..),
    EmbedField (..),
  ) where
  
  import Calamity.Internal.IntColour (IntColour (..))
  import Calamity.Internal.Utils (CalamityToJSON (..), CalamityToJSON' (..), (.=), (.?=))
  import Data.Aeson ((.!=), (.:), (.:?))
  import Data.Aeson qualified as Aeson
  import Data.Colour (Colour)
  import Data.Default.Class
  import Data.Semigroup
  import Data.Text (Text)
  import Data.Time
  import Data.Word
  import Optics ((%~), (&), (^.))
  import Optics.TH
  import TextShow qualified
  import TextShow.TH
  
  data Embed = Embed
    { title :: Maybe Text
    , type_ :: Maybe Text
    , description :: Maybe Text
    , url :: Maybe Text
    , timestamp :: Maybe UTCTime
    , color :: Maybe (Colour Double)
    , footer :: Maybe EmbedFooter
    , image :: Maybe EmbedImage
    , thumbnail :: Maybe EmbedThumbnail
    , video :: Maybe EmbedVideo
    , provider :: Maybe EmbedProvider
    , author :: Maybe EmbedAuthor
    , fields :: [EmbedField]
    }
    deriving (Eq, Show)
    deriving (TextShow.TextShow) via TextShow.FromStringShow Embed
    deriving (Aeson.ToJSON) via CalamityToJSON Embed
  
  instance Default Embed where
    def =
      Embed
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        Nothing
        []
  
  instance CalamityToJSON' Embed where
    toPairs Embed {..} =
      [ "title" .?= title
      , "type" .?= type_
      , "description" .?= description
      , "url" .?= url
      , "timestamp" .?= timestamp
      , "color" .?= (IntColour <$> color)
      , "footer" .?= footer
      , "image" .?= image
      , "thumbnail" .?= thumbnail
      , "video" .?= video
      , "provider" .?= provider
      , "author" .?= author
      , "fields" .= fields
      ]
  
  instance Aeson.FromJSON Embed where
    parseJSON = Aeson.withObject "Embed" $ \v ->
      Embed
        <$> v .:? "title"
        <*> v .:? "type"
        <*> v .:? "description"
        <*> v .:? "url"
        <*> v .:? "timestamp"
        <*> (fmap fromIntColour <$> v .:? "color")
        <*> v .:? "footer"
        <*> v .:? "image"
        <*> v .:? "thumbnail"
        <*> v .:? "video"
        <*> v .:? "provider"
        <*> v .:? "author"
        <*> v .:? "fields" .!= []
  
  data EmbedFooter = EmbedFooter
    { text :: Text
    , iconUrl :: Maybe Text
    , proxyIconUrl :: Maybe Text
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedFooter
  
  instance CalamityToJSON' EmbedFooter where
    toPairs EmbedFooter {..} =
      [ "text" .= text
      , "icon_url" .?= iconUrl
      , "proxy_icon_url" .?= proxyIconUrl
      ]
  
  instance Aeson.FromJSON EmbedFooter where
    parseJSON = Aeson.withObject "EmbedFooter" $ \v ->
      EmbedFooter
        <$> v .: "text"
        <*> v .:? "icon_url"
        <*> v .:? "proxy_icon_url"
  
  {- | Create an embed footer with a provided content
  
   The remaining fields are set to Nothing
  -}
  embedFooter :: Text -> EmbedFooter
  embedFooter text = EmbedFooter text Nothing Nothing
  
  data EmbedImage = EmbedImage
    { url :: Text
    , proxyUrl :: Maybe Text
    , width :: Maybe Word64
    , height :: Maybe Word64
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedImage
  
  instance CalamityToJSON' EmbedImage where
    toPairs EmbedImage {..} =
      [ "url" .= url
      , "proxy_url" .= proxyUrl
      , "width" .= width
      , "height" .= height
      ]
  
  instance Aeson.FromJSON EmbedImage where
    parseJSON = Aeson.withObject "EmbedImage" $ \v ->
      EmbedImage
        <$> v .: "url"
        <*> v .:? "proxy_url"
        <*> v .:? "width"
        <*> v .:? "height"
  
  {- | Create an embed image with a provided url
  
   The remaining fields are set to Nothing
  -}
  embedImage :: Text -> EmbedImage
  embedImage url = EmbedImage url Nothing Nothing Nothing
  
  data EmbedThumbnail = EmbedThumbnail
    { url :: Text
    , proxyUrl :: Maybe Text
    , width :: Maybe Word64
    , height :: Maybe Word64
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedThumbnail
  
  instance CalamityToJSON' EmbedThumbnail where
    toPairs EmbedThumbnail {..} =
      [ "url" .= url
      , "proxy_url" .?= proxyUrl
      , "width" .?= width
      , "height" .?= height
      ]
  
  instance Aeson.FromJSON EmbedThumbnail where
    parseJSON = Aeson.withObject "EmbedThumbnail" $ \v ->
      EmbedThumbnail
        <$> v .: "url"
        <*> v .:? "proxy_url"
        <*> v .:? "width"
        <*> v .:? "height"
  
  {- | Create an embed thumbnail with a provided url
  
   The remaining fields are set to Nothing
  -}
  embedThumbnail :: Text -> EmbedThumbnail
  embedThumbnail url = EmbedThumbnail url Nothing Nothing Nothing
  
  data EmbedVideo = EmbedVideo
    { url :: Maybe Text
    , proxyUrl :: Maybe Text
    , width :: Maybe Word64
    , height :: Maybe Word64
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedVideo
  
  instance CalamityToJSON' EmbedVideo where
    toPairs EmbedVideo {..} =
      [ "url" .= url
      , "proxy_url" .= proxyUrl
      , "width" .= width
      , "height" .= height
      ]
  
  instance Aeson.FromJSON EmbedVideo where
    parseJSON = Aeson.withObject "EmbedVideo" $ \v ->
      EmbedVideo
        <$> v .: "url"
        <*> v .:? "proxy_url"
        <*> v .:? "width"
        <*> v .:? "height"
  
  data EmbedProvider = EmbedProvider
    { name :: Maybe Text
    , url :: Maybe Text
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedProvider
  
  instance CalamityToJSON' EmbedProvider where
    toPairs EmbedProvider {..} =
      [ "name" .= name
      , "url" .= url
      ]
  
  instance Aeson.FromJSON EmbedProvider where
    parseJSON = Aeson.withObject "EmbedProvider" $ \v ->
      EmbedProvider
        <$> v .:? "name"
        <*> v .:? "url"
  
  data EmbedAuthor = EmbedAuthor
    { name :: Maybe Text
    , url :: Maybe Text
    , iconUrl :: Maybe Text
    , proxyIconUrl :: Maybe Text
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedAuthor
  
  instance Default EmbedAuthor where
    def = EmbedAuthor Nothing Nothing Nothing Nothing
  
  instance CalamityToJSON' EmbedAuthor where
    toPairs EmbedAuthor {..} =
      [ "name" .?= name
      , "url" .?= url
      , "icon_url" .?= iconUrl
      , "proxy_icon_url" .?= proxyIconUrl
      ]
  
  instance Aeson.FromJSON EmbedAuthor where
    parseJSON = Aeson.withObject "EmbedAuthor" $ \v ->
      EmbedAuthor
        <$> v .:? "name"
        <*> v .:? "url"
        <*> v .:? "icon_url"
        <*> v .:? "proxy_icon_url"
  
  {- | Create an embed author with the given name
  
   The remaining fields are set to Nothing
  -}
  embedAuthor :: Text -> EmbedAuthor
  embedAuthor name = EmbedAuthor (Just name) Nothing Nothing Nothing
  
  {- | Create an embed author with the given name, url, and icon url
  
   The remaining fields are set to Nothing
  -}
  embedAuthor' ::
    -- | Name
    Text ->
    -- | Url
    Text ->
    -- | Icon url
    Text ->
    EmbedAuthor
  embedAuthor' name url iconUrl = EmbedAuthor (Just name) (Just url) (Just iconUrl) Nothing
  
  data EmbedField = EmbedField
    { name :: Text
    , value :: Text
    , inline :: Bool
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON EmbedField
  
  instance CalamityToJSON' EmbedField where
    toPairs EmbedField {..} =
      [ "name" .= name
      , "value" .= value
      , "inline" .= inline
      ]
  
  instance Aeson.FromJSON EmbedField where
    parseJSON = Aeson.withObject "EmbedField" $ \v ->
      EmbedField
        <$> v .: "name"
        <*> v .: "value"
        <*> v .:? "inline" .!= False
  
  -- | Create a non-inline embed field
  embedField ::
    -- | Name
    Text ->
    -- | Value
    Text ->
    EmbedField
  embedField name value = EmbedField name value False
  
  $(makeFieldLabelsNoPrefix ''Embed)
  
  $(deriveTextShow ''EmbedFooter)
  $(makeFieldLabelsNoPrefix ''EmbedFooter)
  
  $(deriveTextShow ''EmbedImage)
  $(makeFieldLabelsNoPrefix ''EmbedImage)
  
  $(deriveTextShow ''EmbedThumbnail)
  $(makeFieldLabelsNoPrefix ''EmbedThumbnail)
  
  $(deriveTextShow ''EmbedVideo)
  $(makeFieldLabelsNoPrefix ''EmbedVideo)
  
  $(deriveTextShow ''EmbedProvider)
  $(makeFieldLabelsNoPrefix ''EmbedProvider)
  
  $(deriveTextShow ''EmbedAuthor)
  $(makeFieldLabelsNoPrefix ''EmbedAuthor)
  
  $(deriveTextShow ''EmbedField)
  $(makeFieldLabelsNoPrefix ''EmbedField)
  
  instance Semigroup EmbedFooter where
    l <> r =
      l
        & #text
        %~ (<> (r ^. #text))
        & #iconUrl
        %~ getLast
        . (<> Last (r ^. #iconUrl))
        . Last
        & #proxyIconUrl
        %~ getLast
        . (<> Last (r ^. #proxyIconUrl))
        . Last
  
  instance Semigroup Embed where
    l <> r =
      l
        & #title
        %~ (<> (r ^. #title))
        & #type_
        %~ getLast
        . (<> Last (r ^. #type_))
        . Last
        & #description
        %~ (<> (r ^. #description))
        & #url
        %~ getLast
        . (<> Last (r ^. #url))
        . Last
        & #timestamp
        %~ getLast
        . (<> Last (r ^. #timestamp))
        . Last
        & #color
        %~ getLast
        . (<> Last (r ^. #color))
        . Last
        & #footer
        %~ (<> (r ^. #footer))
        & #image
        %~ getLast
        . (<> Last (r ^. #image))
        . Last
        & #thumbnail
        %~ getLast
        . (<> Last (r ^. #thumbnail))
        . Last
        & #video
        %~ getLast
        . (<> Last (r ^. #video))
        . Last
        & #provider
        %~ getLast
        . (<> Last (r ^. #provider))
        . Last
        & #author
        %~ getLast
        . (<> Last (r ^. #author))
        . Last
        & #fields
        %~ (<> (r ^. #fields))
  
  instance Monoid Embed where
    mempty = def
  