content: {-# LANGUAGE TemplateHaskell #-}
  
  -- | User activities
  module Calamity.Types.Model.Presence.Activity (
    Activity (..),
    activity,
    ActivityType (..),
    ActivityTimestamps (..),
    ActivityParty (..),
    ActivityAssets (..),
    ActivitySecrets (..),
  ) where
  
  import Calamity.Internal.UnixTimestamp
  import Calamity.Internal.Utils
  import Calamity.Types.Snowflake
  import Data.Aeson ((.:), (.:?))
  import Data.Aeson qualified as Aeson
  import Data.Scientific
  import Data.Text (Text)
  import Data.Time (UTCTime)
  import Data.Word
  import Optics.TH
  import TextShow qualified
  import TextShow.TH
  
  data ActivityType
    = Game
    | Streaming
    | Listening
    | Custom
    | Other Int
    deriving (Eq, Show)
  
  instance Aeson.ToJSON ActivityType where
    toJSON Game = Aeson.Number 0
    toJSON Streaming = Aeson.Number 1
    toJSON Listening = Aeson.Number 2
    toJSON Custom = Aeson.Number 4
    toJSON (Other n) = Aeson.Number $ fromIntegral n
  
  instance Aeson.FromJSON ActivityType where
    parseJSON = Aeson.withScientific "ActivityType" $ \n -> case toBoundedInteger @Int n of
      Just v -> case v of
        0 -> pure Game
        1 -> pure Streaming
        2 -> pure Listening
        4 -> pure Custom
        n -> pure $ Other n
      Nothing -> fail $ "Invalid ActivityType: " <> show n
  
  data Activity = Activity
    { name :: Text
    , type_ :: ActivityType
    , url :: Maybe Text
    , timestamps :: Maybe ActivityTimestamps
    , applicationID :: Maybe (Snowflake ())
    , details :: Maybe Text
    , state :: Maybe Text
    , party :: Maybe ActivityParty
    , assets :: Maybe ActivityAssets
    , secrets :: Maybe ActivitySecrets
    , instance_ :: Maybe Bool
    , flags :: Maybe Word64
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON Activity
  
  instance CalamityToJSON' Activity where
    toPairs Activity {..} =
      [ "name" .= name
      , "type" .= type_
      , "url" .?= url
      , "timestamps" .?= timestamps
      , "application_id" .?= applicationID
      , "details" .?= details
      , "state" .?= state
      , "party" .?= party
      , "assets" .?= assets
      , "secrets" .?= secrets
      , "instance" .?= instance_
      , "flags" .?= flags
      ]
  
  instance Aeson.FromJSON Activity where
    parseJSON = Aeson.withObject "Activity" $ \v ->
      Activity
        <$> v .: "name"
        <*> v .: "type"
        <*> v .:? "url"
        <*> v .:? "timestamps"
        <*> v .:? "application_id"
        <*> v .:? "details"
        <*> v .:? "state"
        <*> v .:? "party"
        <*> v .:? "assets"
        <*> v .:? "secrets"
        <*> v .:? "instance"
        <*> v .:? "flags"
  
  -- | Make an 'Activity' with all optional fields set to Nothing
  activity :: Text -> ActivityType -> Activity
  activity !name !type_ =
    Activity
      { name = name
      , type_ = type_
      , url = Nothing
      , timestamps = Nothing
      , applicationID = Nothing
      , details = Nothing
      , state = Nothing
      , party = Nothing
      , assets = Nothing
      , secrets = Nothing
      , instance_ = Nothing
      , flags = Nothing
      }
  
  data ActivityTimestamps = ActivityTimestamps
    { start :: Maybe UTCTime
    , end :: Maybe UTCTime
    }
    deriving (Eq, Show)
    deriving (TextShow.TextShow) via TextShow.FromStringShow ActivityTimestamps
    deriving (Aeson.ToJSON) via CalamityToJSON ActivityTimestamps
  
  instance CalamityToJSON' ActivityTimestamps where
    toPairs ActivityTimestamps {..} =
      [ "start" .?= fmap UnixTimestamp start
      , "end" .?= fmap UnixTimestamp end
      ]
  
  instance Aeson.FromJSON ActivityTimestamps where
    parseJSON = Aeson.withObject "ActivityTimestamps" $ \v ->
      ActivityTimestamps
        <$> (fmap unUnixTimestamp <$> v .:? "start")
        <*> (fmap unUnixTimestamp <$> v .:? "end")
  
  data ActivityParty = ActivityParty
    { id :: Maybe Text
    , size :: Maybe (Int, Int)
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON ActivityParty
  
  instance CalamityToJSON' ActivityParty where
    toPairs ActivityParty {..} =
      [ "id" .?= id
      , "size" .?= size
      ]
  
  instance Aeson.FromJSON ActivityParty where
    parseJSON = Aeson.withObject "ActivityParty" $ \v ->
      ActivityParty
        <$> v .:? "id"
        <*> v .:? "size"
  
  data ActivityAssets = ActivityAssets
    { largeImage :: Maybe Text
    , largeText :: Maybe Text
    , smallImage :: Maybe Text
    , smallText :: Maybe Text
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON ActivityAssets
  
  instance CalamityToJSON' ActivityAssets where
    toPairs ActivityAssets {..} =
      [ "large_image" .?= largeImage
      , "large_text" .?= largeText
      , "small_image" .?= smallImage
      , "small_text" .?= smallText
      ]
  
  instance Aeson.FromJSON ActivityAssets where
    parseJSON = Aeson.withObject "ActivityAssets" $ \v ->
      ActivityAssets
        <$> v .:? "large_image"
        <*> v .:? "large_text"
        <*> v .:? "small_image"
        <*> v .:? "small_text"
  
  data ActivitySecrets = ActivitySecrets
    { join :: Maybe Text
    , spectate :: Maybe Text
    , match :: Maybe Text
    }
    deriving (Eq, Show)
    deriving (Aeson.ToJSON) via CalamityToJSON ActivitySecrets
  
  instance CalamityToJSON' ActivitySecrets where
    toPairs ActivitySecrets {..} =
      [ "join" .?= join
      , "spectate" .?= spectate
      , "match" .?= match
      ]
  
  instance Aeson.FromJSON ActivitySecrets where
    parseJSON = Aeson.withObject "ActivitySecrets" $ \v ->
      ActivitySecrets
        <$> v .:? "join"
        <*> v .:? "spectate"
        <*> v .:? "match"
  
  $(deriveTextShow ''ActivityType)
  $(deriveTextShow ''ActivityParty)
  $(deriveTextShow ''ActivityAssets)
  $(deriveTextShow ''ActivitySecrets)
  $(deriveTextShow ''Activity)
  $(makeFieldLabelsNoPrefix ''Activity)
  $(makeFieldLabelsNoPrefix ''ActivityTimestamps)
  $(makeFieldLabelsNoPrefix ''ActivityParty)
  $(makeFieldLabelsNoPrefix ''ActivityAssets)
  $(makeFieldLabelsNoPrefix ''ActivitySecrets)
  