/** Types for: https://docs.discord.com/developers/resources/message */

import type { DiscordApplication } from './application.js';
import type { DiscordChannel, DiscordThreadMember } from './channel.js';
import type { DiscordMessageComponents } from './components.js';
import type { DiscordEmoji } from './emoji.js';
import type { DiscordMessageCreateExtra } from './gateway.js';
import type {
  DiscordAuthorizingIntegrationOwners,
  DiscordInteractionDataResolved,
  DiscordMessageInteraction,
  InteractionTypes,
} from './interactions.js';
import type { DiscordPoll } from './poll.js';
import type { DiscordSticker, DiscordStickerItem } from './sticker.js';
import type { DiscordUser } from './user.js';

/** https://docs.discord.com/developers/resources/message#message-object-message-structure */
export interface DiscordMessage extends Partial<DiscordMessageCreateExtra> {
  /** id of the message */
  id: string;
  /** id of the channel the message was sent in */
  channel_id: string;
  /**
   * The author of this message (not guaranteed to be a valid user)
   *
   * @remarks
   * The author object follows the structure of the user object, but is only a valid user in the case where the message is generated by a user or bot user. If the message is generated by a webhook, the author object corresponds to the webhook's id, username, and avatar. You can tell if a message is generated by a webhook by checking for the webhook_id on the message object.
   */
  author: DiscordUser;
  /** Contents of the message */
  content: string;
  /** When this message was sent */
  timestamp: string;
  /** When this message was edited (or null if never) */
  edited_timestamp: string | null;
  /** Whether this was a TTS message */
  tts: boolean;
  /** Whether this message mentions everyone */
  mention_everyone: boolean;
  // TODO: When we separate the types between with extra fields from gateway and non, we should add back the mentions field below
  //       For now it can remain from the gateway extra fields to avoid breaking changes for now.
  // /** Users specifically mentioned in the message */
  // mentions: DiscordUser[]
  /** Roles specifically mentioned in this message */
  mention_roles: string[];
  /**
   * Channels specifically mentioned in this message
   * Note: Not all channel mentions in a message will appear in `mention_channels`. Only textual channels that are visible to everyone in a discoverable guild will ever be included. Only crossposted messages (via Channel Following) currently include `mention_channels` at all. If no mentions in the message meet these requirements, this field will not be sent.
   */
  mention_channels?: DiscordChannelMention[];
  /** Any attached files */
  attachments: DiscordAttachment[];
  /** Any embedded content */
  embeds: DiscordEmbed[];
  /** Reactions to the message */
  reactions?: DiscordReaction[];
  /** Used for validating a message was sent */
  nonce?: number | string;
  /** Whether this message is pinned */
  pinned: boolean;
  /** If the message is generated by a webhook, this is the webhook's id */
  webhook_id?: string;
  /** Type of message */
  type: MessageTypes;
  /** Sent with Rich Presence-related chat embeds */
  activity?: DiscordMessageActivity;
  /** Sent with Rich Presence-related chat embeds */
  application?: Partial<DiscordApplication>;
  /** if the message is an Interaction or application-owned webhook, this is the id of the application */
  application_id?: string;
  /** Message flags combined as a bitfield */
  flags?: MessageFlags;
  /** Data showing the source of a crosspost, channel follow add, pin, or reply message */
  message_reference?: Omit<DiscordMessageReference, 'failIfNotExists'>;
  /** The message associated with the `message_reference`. This is a minimal subset of fields in a message (e.g. `author` is excluded.)  */
  message_snapshots?: DiscordMessageSnapshot[];
  /**
   * The message associated with the 'message_reference'
   *
   * @remarks
   * This field is only returned for messages with a 'type' of '19', '21', or '23'. If the message is one of these but the 'referenced_message' field is not present, the backend did not attempt to fetch the message that was being replied to, so its state is unknown. If the field exists but is null, the referenced message was deleted.
   */
  referenced_message?: DiscordMessage | null;
  /** sent if the message is sent as a result of an interaction */
  interaction_metadata?: DiscordMessageInteractionMetadata;
  /**
   * Sent if the message is a response to an Interaction
   *
   * @deprecated Deprecated in favor of {@link interaction_metadata}
   */
  interaction?: DiscordMessageInteraction;
  /** The thread that was started from this message, includes thread member object */
  thread?: Omit<DiscordChannel, 'member'> & { member: DiscordThreadMember };
  /** The components related to this message */
  components?: DiscordMessageComponents;
  /** Sent if the message contains stickers */
  sticker_items?: DiscordStickerItem[];
  /**
   * The stickers sent with the message (bots currently can only receive messages with stickers, not send)
   * @deprecated
   */
  stickers?: DiscordSticker[];
  /** A generally increasing integer (there may be gaps or duplicates) that represents the approximate position of the message in a thread, it can be used to estimate the relative position of the message in a thread in company with `total_message_sent` on parent thread */
  position?: number;
  /** data of the role subscription purchase or renewal that prompted this ROLE_SUBSCRIPTION_PURCHASE message */
  role_subscription_data?: DiscordRoleSubscriptionData;
  /** data for users, members, channels, and roles referenced in this message */
  resolved?: DiscordInteractionDataResolved;
  /** The poll object */
  poll?: DiscordPoll;
  /** The call associated with the message */
  call?: DiscordMessageCall;
}

/** https://docs.discord.com/developers/resources/message#message-object-message-types */
export enum MessageTypes {
  Default,
  RecipientAdd,
  RecipientRemove,
  Call,
  ChannelNameChange,
  ChannelIconChange,
  ChannelPinnedMessage,
  UserJoin,
  GuildBoost,
  GuildBoostTier1,
  GuildBoostTier2,
  GuildBoostTier3,
  ChannelFollowAdd,
  GuildDiscoveryDisqualified = 14,
  GuildDiscoveryRequalified,
  GuildDiscoveryGracePeriodInitialWarning,
  GuildDiscoveryGracePeriodFinalWarning,
  ThreadCreated,
  Reply,
  ChatInputCommand,
  ThreadStarterMessage,
  GuildInviteReminder,
  ContextMenuCommand,
  AutoModerationAction,
  RoleSubscriptionPurchase,
  InteractionPremiumUpsell,
  StageStart,
  StageEnd,
  StageSpeaker,
  StageTopic = 31,
  GuildApplicationPremiumSubscription,
  GuildIncidentAlertModeEnabled = 36,
  GuildIncidentAlertModeDisabled,
  GuildIncidentReportRaid,
  GuildIncidentReportFalseAlarm,
  PurchaseNotification = 44,
  PollResult = 46,
}

/** https://docs.discord.com/developers/resources/message#message-object-message-activity-structure */
export interface DiscordMessageActivity {
  /** Type of message activity */
  type: MessageActivityTypes;
  /** `party_id` from a Rich Presence event */
  party_id?: string;
}

/** https://docs.discord.com/developers/resources/message#message-object-message-activity-types */
export enum MessageActivityTypes {
  Join = 1,
  Spectate = 2,
  Listen = 3,
  JoinRequest = 5,
}

/** https://docs.discord.com/developers/resources/message#message-object-message-flags */
export enum MessageFlags {
  /** This message has been published to subscribed channels (via Channel Following) */
  Crossposted = 1 << 0,
  /** This message originated from a message in another channel (via Channel Following) */
  IsCrosspost = 1 << 1,
  /** Do not include any embeds when serializing this message */
  SuppressEmbeds = 1 << 2,
  /** The source message for this crosspost has been deleted (via Channel Following) */
  SourceMessageDeleted = 1 << 3,
  /** This message came from the urgent message system */
  Urgent = 1 << 4,
  /** This message has an associated thread, with the same id as the message */
  HasThread = 1 << 5,
  /** This message is only visible to the user who invoked the Interaction */
  Ephemeral = 1 << 6,
  /** This message is an Interaction Response and the bot is "thinking" */
  Loading = 1 << 7,
  /** This message failed to mention some roles and add their members to the thread */
  FailedToMentionSomeRolesInThread = 1 << 8,
  /** This message will not trigger push and desktop notifications */
  SuppressNotifications = 1 << 12,
  /** This message is a voice message */
  IsVoiceMessage = 1 << 13,
  /** This message has a snapshot (via Message Forwarding) */
  HasSnapshot = 1 << 14,
  /**
   * Allows you to create fully component-driven messages
   *
   * @remarks
   * Once a message has been sent with this flag, it can't be removed from that message.
   */
  IsComponentsV2 = 1 << 15,
}

/** https://docs.discord.com/developers/resources/message#message-interaction-metadata-object */
export type DiscordMessageInteractionMetadata =
  | DiscordApplicationCommandInteractionMetadata
  | DiscordMessageComponentInteractionMetadata
  | DiscordModalSubmitInteractionMetadata;

/** https://docs.discord.com/developers/resources/message#message-interaction-metadata-object-application-command-interaction-metadata-structure */
export interface DiscordApplicationCommandInteractionMetadata {
  /** Id of the interaction */
  id: string;
  /** The type of interaction */
  type: InteractionTypes;
  /** User who triggered the interaction */
  user: DiscordUser;
  /** IDs for installation context(s) related to an interaction */
  authorizing_integration_owners: DiscordAuthorizingIntegrationOwners;
  /** ID of the original response message, present only on follow-up messages */
  original_response_message_id?: string;
  /** The user the command was run on, present only on user command interactions */
  target_user?: DiscordUser;
  /** The ID of the message the command was run on, present only on message command interactions. The original response message will also have message_reference and referenced_message pointing to this message. */
  target_message_id?: string;
}

/** https://docs.discord.com/developers/resources/message#message-interaction-metadata-object-message-component-interaction-metadata-structure */
export interface DiscordMessageComponentInteractionMetadata {
  /** Id of the interaction */
  id: string;
  /** The type of interaction */
  type: InteractionTypes;
  /** User who triggered the interaction */
  user: DiscordUser;
  /** IDs for installation context(s) related to an interaction */
  authorizing_integration_owners: DiscordAuthorizingIntegrationOwners;
  /** ID of the original response message, present only on follow-up messages */
  original_response_message_id?: string;
  /** ID of the message that contained interactive component, present only on messages created from component interactions */
  interacted_message_id: string;
}

/** https://docs.discord.com/developers/resources/message#message-interaction-metadata-object-modal-submit-interaction-metadata-structure */
export interface DiscordModalSubmitInteractionMetadata {
  /** Id of the interaction */
  id: string;
  /** The type of interaction */
  type: InteractionTypes;
  /** User who triggered the interaction */
  user: DiscordUser;
  /** IDs for installation context(s) related to an interaction */
  authorizing_integration_owners: DiscordAuthorizingIntegrationOwners;
  /** ID of the original response message, present only on follow-up messages */
  original_response_message_id?: string;
  /** Metadata for the interaction that was used to open the modal, present only on modal submit interactions */
  triggering_interaction_metadata: DiscordApplicationCommandInteractionMetadata | DiscordMessageComponentInteractionMetadata;
}

/** https://docs.discord.com/developers/resources/message#message-call-object-message-call-object-structure */
export interface DiscordMessageCall {
  /** Array of user object ids that participated in the call */
  participants: string[];
  /** Time when call ended */
  ended_timestamp?: string | null;
}

/** https://docs.discord.com/developers/resources/message#message-reference-structure */
export interface DiscordMessageReference {
  /** Type of reference */
  type?: DiscordMessageReferenceType;
  /** id of the originating message */
  message_id?: string;
  /**
   * id of the originating message's channel
   * Note: `channel_id` is optional when creating a reply, but will always be present when receiving an event/response that includes this data model.
   */
  channel_id?: string;
  /** id of the originating message's guild */
  guild_id?: string;
  /** When sending, whether to error if the referenced message doesn't exist instead of sending as a normal (non-reply) message, default true */
  fail_if_not_exists?: boolean;
}

/** https://docs.discord.com/developers/resources/message#message-reference-types */
export enum DiscordMessageReferenceType {
  /**
   * A standard reference used by replies.
   *
   * @remarks
   * When the type is set to this value, the field referenced_message on the message will be present
   */
  Default,
  /**
   * Reference used to point to a message at a point in time.
   *
   * @remarks
   * When the type is set to this value, the field message_snapshot on the message will be present
   *
   * This value can only be used for basic messages;
   * i.e. messages which do not have strong bindings to a non global entity.
   * Thus we support only messages with type `DEFAULT`, `REPLY`, `CHAT_INPUT_COMMAND`, or `CONTEXT_MENU_COMMAND`, and don't support messages with a poll, call, or activity.
   */
  Forward,
}

/** https://docs.discord.com/developers/resources/message#message-snapshot-structure */
export interface DiscordMessageSnapshot {
  /** Minimal subset of fields in the forwarded message */
  message: Pick<
    DiscordMessage,
    | 'type'
    | 'content'
    | 'embeds'
    | 'attachments'
    | 'timestamp'
    | 'edited_timestamp'
    | 'flags'
    | 'mentions'
    | 'mention_roles'
    | 'stickers'
    | 'sticker_items'
    | 'components'
  >;
}

/** https://docs.discord.com/developers/resources/message#reaction-object-reaction-structure */
export interface DiscordReaction {
  /** Total number of times this emoji has been used to react (including super reacts) */
  count: number;
  /**	Reaction count details object */
  count_details: DiscordReactionCountDetails;
  /** Whether the current user reacted using this emoji */
  me: boolean;
  /**	Whether the current user super-reacted using this emoji */
  me_burst: boolean;
  /** Emoji information */
  emoji: Partial<DiscordEmoji>;
  /** HEX colors used for super reaction */
  burst_colors: string[];
}

/** https://docs.discord.com/developers/resources/message#reaction-count-details-object-reaction-count-details-structure */
export interface DiscordReactionCountDetails {
  /** Count of super reactions */
  burst: number;
  /**	Count of normal reactions */
  normal: number;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-structure */
export interface DiscordEmbed {
  /** Title of embed */
  title?: string;
  /** Type of embed (always "rich" for webhook embeds) */
  type?: EmbedTypes;
  /** Description of embed */
  description?: string;
  /** Url of embed */
  url?: string;
  /** Timestamp of embed content */
  timestamp?: string;
  /** Color code of the embed */
  color?: number;
  /** Footer information */
  footer?: DiscordEmbedFooter;
  /** Image information */
  image?: DiscordEmbedImage;
  /** Thumbnail information */
  thumbnail?: DiscordEmbedThumbnail;
  /** Video information */
  video?: DiscordEmbedVideo;
  /** Provider information */
  provider?: DiscordEmbedProvider;
  /** Author information */
  author?: DiscordEmbedAuthor;
  /** Fields information */
  fields?: DiscordEmbedField[];
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-types */
export type EmbedTypes = 'rich' | 'image' | 'video' | 'gifv' | 'article' | 'link' | 'poll_result';

/** https://docs.discord.com/developers/resources/message#embed-object-embed-thumbnail-structure */
export interface DiscordEmbedThumbnail {
  /** Source url of thumbnail (only supports http(s) and attachments) */
  url: string;
  /** A proxied url of the thumbnail */
  proxy_url?: string;
  /** Height of thumbnail */
  height?: number;
  /** Width of thumbnail */
  width?: number;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-video-structure */
export interface DiscordEmbedVideo {
  /** Source url of video */
  url?: string;
  /** A proxied url of the video */
  proxy_url?: string;
  /** Height of video */
  height?: number;
  /** Width of video */
  width?: number;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-image-structure */
export interface DiscordEmbedImage {
  /** Source url of image (only supports http(s) and attachments) */
  url: string;
  /** A proxied url of the image */
  proxy_url?: string;
  /** Height of image */
  height?: number;
  /** Width of image */
  width?: number;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-provider-structure */
export interface DiscordEmbedProvider {
  /** Name of provider */
  name?: string;
  /** Url of provider */
  url?: string;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-author-structure */
export interface DiscordEmbedAuthor {
  /** Name of author */
  name: string;
  /** Url of author */
  url?: string;
  /** Url of author icon (only supports http(s) and attachments) */
  icon_url?: string;
  /** A proxied url of author icon */
  proxy_icon_url?: string;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-footer-structure */
export interface DiscordEmbedFooter {
  /** Footer text */
  text: string;
  /** Url of footer icon (only supports http(s) and attachments) */
  icon_url?: string;
  /** A proxied url of footer icon */
  proxy_icon_url?: string;
}

/** https://docs.discord.com/developers/resources/message#embed-object-embed-field-structure */
export interface DiscordEmbedField {
  /** Name of the field */
  name: string;
  /** Value of the field */
  value: string;
  /** Whether or not this field should display inline */
  inline?: boolean;
}

/** https://docs.discord.com/developers/resources/message#attachment-object-attachment-structure */
export interface DiscordAttachment {
  /** Attachment id */
  id: string;
  /** Name of file attached */
  filename: string;
  /** The title of the file */
  title?: string;
  /** description for the file (max 1024 characters) */
  description?: string;
  /** The attachment's [media type](https://en.wikipedia.org/wiki/Media_type) */
  content_type?: string;
  /** Size of file in bytes */
  size: number;
  /** Source url of file */
  url: string;
  /** A proxied url of file */
  proxy_url: string;
  /** Height of file (if image) */
  height?: number | null;
  /** Width of file (if image) */
  width?: number | null;
  /** whether this attachment is ephemeral. Ephemeral attachments will automatically be removed after a set period of time. Ephemeral attachments on messages are guaranteed to be available as long as the message itself exists. */
  ephemeral?: boolean;
  /** The duration of the audio file for a voice message */
  duration_secs?: number;
  /** A base64 encoded bytearray representing a sampled waveform for a voice message */
  waveform?: string;
  /** Attachment flags combined as a bitfield */
  flags?: AttachmentFlags;
}

/** https://docs.discord.com/developers/resources/message#attachment-object-attachment-flags */
export enum AttachmentFlags {
  None,
  /** This attachment has been edited using the remix feature on mobile */
  IsRemix = 1 << 2,
}

/** https://docs.discord.com/developers/resources/message#channel-mention-object-channel-mention-structure */
export interface DiscordChannelMention {
  /** id of the channel */
  id: string;
  /** id of the guild containing the channel */
  guild_id: string;
  /** The type of channel */
  type: number;
  /** The name of the channel */
  name: string;
}

/** https://docs.discord.com/developers/resources/message#allowed-mentions-object-allowed-mention-types */
export enum AllowedMentionsTypes {
  /** Controls role mentions */
  RoleMentions = 'roles',
  /** Controls user mentions */
  UserMentions = 'users',
  /** Controls \@everyone and \@here mentions */
  EveryoneMentions = 'everyone',
}

/** https://docs.discord.com/developers/resources/message#allowed-mentions-object-allowed-mentions-structure */
export interface DiscordAllowedMentions {
  /** An array of allowed mention types to parse from the content. */
  parse?: AllowedMentionsTypes[];
  /** Array of role_ids to mention (Max size of 100) */
  roles?: string[];
  /** Array of user_ids to mention (Max size of 100) */
  users?: string[];
  /** For replies, whether to mention the author of the message being replied to (default false) */
  replied_user?: boolean;
}

/** https://docs.discord.com/developers/resources/message#role-subscription-data-object-role-subscription-data-object-structure */
export interface DiscordRoleSubscriptionData {
  /** the id of the sku and listing that the user is subscribed to */
  role_subscription_listing_id: string;
  /** the name of the tier that the user is subscribed to */
  tier_name: string;
  /** the cumulative number of months that the user has been subscribed  */
  total_months_subscribed: number;
  /** whether this notification is for a renewal rather than a new purchase */
  is_renewal: boolean;
}

/** https://docs.discord.com/developers/resources/message#message-pin-object-message-pin-object-struture */
export interface DiscordMessagePin {
  /** the time the message was pinned */
  pinned_at: string;
  /** the pinned message */
  message: DiscordMessage;
}

/** https://docs.discord.com/developers/resources/message#get-reactions-reaction-types */
export enum DiscordReactionType {
  Normal,
  Burst,
}

/** https://docs.discord.com/developers/resources/message#get-channel-pins-response-structure */
export interface DiscordGetChannelPins {
  items: DiscordMessagePin[];
  has_more: boolean;
}
