content: {-# LANGUAGE TemplateHaskell #-}
  
  -- | Discord Interactions
  module Calamity.Types.Model.Interaction (
    Interaction (..),
    InteractionToken (..),
    InteractionData (..),
    ResolvedInteractionData (..),
    InteractionType (..),
    Application,
    ApplicationCommand,
  ) where
  
  import Calamity.Types.Model.Channel (Attachment, Channel, Partial)
  import Calamity.Types.Model.Channel.Component
  import Calamity.Types.Model.Channel.Message (Message)
  import Calamity.Types.Model.Guild (Guild, Role)
  import Calamity.Types.Model.Guild.Member (Member)
  import Calamity.Types.Model.User (User)
  import Calamity.Types.Snowflake
  import Data.Aeson ((.!=), (.:), (.:?))
  import Data.Aeson qualified as Aeson
  import Data.HashMap.Strict qualified as H
  import Data.Scientific (toBoundedInteger)
  import Data.Text qualified as T
  import Optics.TH
  import TextShow qualified
  import TextShow.TH
  
  -- | Empty type to flag application IDs
  data Application
  
  -- | Empty type to flag application command IDs
  data ApplicationCommand
  
  newtype InteractionToken = InteractionToken
    { fromInteractionToken :: T.Text
    }
    deriving stock (Show)
    deriving (Aeson.FromJSON, Aeson.ToJSON) via T.Text
  
  data Interaction = Interaction
    { id :: Snowflake Interaction
    , applicationID :: Snowflake Application
    , type_ :: InteractionType
    , data_ :: Maybe InteractionData
    , guildID :: Maybe (Snowflake Guild)
    , channelID :: Maybe (Snowflake Channel)
    , member :: Maybe Member
    , user :: Maybe User
    , token :: InteractionToken
    , version :: Int
    , message :: Maybe Message
    , locale :: Maybe T.Text
    , guildLocale :: Maybe T.Text
    }
    deriving (Show)
    deriving (HasID Interaction) via HasIDField "id" Interaction
    deriving (HasID Application) via HasIDField "applicationID" Interaction
  
  instance Aeson.FromJSON Interaction where
    parseJSON = Aeson.withObject "Interaction" $ \v ->
      Interaction
        <$> v .: "id"
        <*> v .: "application_id"
        <*> v .: "type"
        <*> v .:? "data"
        <*> v .:? "guild_id"
        <*> v .:? "channel_id"
        <*> v .:? "member"
        <*> v .:? "user"
        <*> v .: "token"
        <*> v .: "version"
        <*> v .:? "message"
        <*> v .:? "locale"
        <*> v .:? "guild_locale"
  
  data InteractionData = InteractionData
    { id :: Maybe (Snowflake ApplicationCommand)
    , name :: Maybe T.Text
    , resolved :: Maybe ResolvedInteractionData
    , -- , options :: [ApplicationCommandInteractionDataOptions]
      -- No commands yet
      customID :: Maybe CustomID
    , componentType :: Maybe ComponentType
    , values :: Maybe [T.Text]
    , targetID :: Maybe (Snowflake ())
    , components :: Maybe [Aeson.Value]
    }
    deriving (Show)
    deriving (TextShow.TextShow) via TextShow.FromStringShow InteractionData
  
  instance Aeson.FromJSON InteractionData where
    parseJSON = Aeson.withObject "InteractionData" $ \v ->
      InteractionData
        <$> v .:? "id"
        <*> v .:? "name"
        <*> v .:? "resolved"
        <*> v .:? "custom_id"
        <*> v .:? "component_type"
        <*> v .:? "values"
        <*> v .:? "target_id"
        <*> v .:? "components"
  
  data ResolvedInteractionData = ResolvedInteractionData
    { users :: H.HashMap (Snowflake User) User
    , members :: H.HashMap (Snowflake Member) Member
    , roles :: H.HashMap (Snowflake Role) Role
    , channels :: H.HashMap (Snowflake Channel) (Partial Channel)
    , messages :: H.HashMap (Snowflake Message) (Partial Message)
    , attachments :: H.HashMap (Snowflake Attachment) Attachment
    }
    deriving (Show)
    deriving (TextShow.TextShow) via TextShow.FromStringShow ResolvedInteractionData
  
  instance Aeson.FromJSON ResolvedInteractionData where
    parseJSON = Aeson.withObject "ResolvedInteractionData" $ \v ->
      ResolvedInteractionData
        <$> v .:? "users" .!= H.empty
        <*> v .:? "members" .!= H.empty
        <*> v .:? "roles" .!= H.empty
        <*> v .:? "channels" .!= H.empty
        <*> v .:? "messages" .!= H.empty
        <*> v .:? "attachments" .!= H.empty
  
  data InteractionType
    = PingType
    | ApplicationCommandType
    | MessageComponentType
    | ApplicationCommandAutoCompleteType
    | ModalSubmitType
    deriving (Eq, Show)
  
  instance Aeson.FromJSON InteractionType where
    parseJSON = Aeson.withScientific "InteractionType" $ \n -> case toBoundedInteger @Int n of
      Just 1 -> pure PingType
      Just 2 -> pure ApplicationCommandType
      Just 3 -> pure MessageComponentType
      Just 4 -> pure ApplicationCommandAutoCompleteType
      Just 5 -> pure ModalSubmitType
      _ -> fail $ "Invalid InteractionType: " <> show n
  
  $(deriveTextShow ''InteractionToken)
  $(deriveTextShow ''InteractionType)
  $(deriveTextShow ''Interaction)
  $(makeFieldLabelsNoPrefix ''InteractionToken)
  $(makeFieldLabelsNoPrefix ''Interaction)
  $(makeFieldLabelsNoPrefix ''InteractionData)
  $(makeFieldLabelsNoPrefix ''ResolvedInteractionData)
  $(makeFieldLabelsNoPrefix ''InteractionType)
  