import { type BigString, type GetGuildWidgetImageQuery, type ImageFormat, type ImageSize, StickerFormatTypes } from '@discordeno/types';
import { iconBigintToHash } from './hash.js';

export interface ImageOptions {
  size?: ImageSize;
  format?: ImageFormat;
}

/** Help format an image url. */
export function formatImageUrl(url: string, size: ImageSize = 128, format?: ImageFormat): string {
  const animated = url.includes('/a_');
  format ??= animated ? 'gif' : 'webp';

  return `${url}.${format}?size=${size}${animated && format === 'webp' ? '&animated=true' : ''}`;
}

/**
 * Get the url for an emoji.
 *
 * @param emojiId The id of the emoji
 * @param animated Whether or not the emoji is animated
 * @param format The format of the image, defaults to png
 * @returns string
 *
 * @remarks
 * The animated parameter is used to specify the animated query parameter valid for webp images or to force the gif if the format is not set to webp.
 */
export function emojiUrl(emojiId: BigString, animated = false, format: ImageFormat = 'png'): string {
  return `https://cdn.discordapp.com/emojis/${emojiId}.${animated ? (format === 'webp' ? 'webp' : 'gif') : format}${animated && format === 'webp' ? '?animated=true' : ''}`;
}

/**
 * Builds a URL to a user's avatar stored in the Discord CDN.
 *
 * @param userId - The ID of the user to get the avatar of.
 * @param avatar - The user's avatar hash.
 * @param options - The parameters for the building of the URL.
 * @returns The user avatar as a URL.
 */
export function avatarUrl(userId: BigString, avatar: BigString, options?: ImageOptions): string {
  return formatImageUrl(
    `https://cdn.discordapp.com/avatars/${userId}/${typeof avatar === 'string' ? avatar : iconBigintToHash(avatar)}`,
    options?.size ?? 128,
    options?.format,
  );
}

/**
 * Builds a URL to a user's default avatar stored in the Discord CDN.
 *
 * @param userId - The ID of the user to get the avatar of.
 * @param discriminator - The user's discriminator. (4-digit tag after the hashtag.)
 * @returns The user default avatar as an URL.
 */
export function defaultAvatarUrl(userId: BigString, discriminator: string) {
  const isLegacy = discriminator === '0' || discriminator === '0000';
  const index = isLegacy ? (BigInt(userId) >> 22n) % 6n : Number(discriminator) % 5;

  return `https://cdn.discordapp.com/embed/avatars/${index}.png`;
}

/**
 * Builds a URL to a user's display avatar stored in the Discord CDN.
 *
 * @param userId - The ID of the user to get the avatar of.
 * @param discriminator - The user's discriminator. (4-digit tag after the hashtag.)
 * @param avatar - The user's avatar hash.
 * @param options - The parameters for the building of the URL.
 * @returns The user display avatar as an URL.
 */
export function displayAvatarUrl(userId: BigString, discriminator: string, avatar: BigString | undefined, options?: ImageOptions): string {
  return avatar ? avatarUrl(userId, avatar, options) : defaultAvatarUrl(userId, discriminator);
}

export function avatarDecorationUrl(avatarDecoration: BigString): string {
  return `https://cdn.discordapp.com/avatar-decoration-presets/${
    typeof avatarDecoration === 'string' ? avatarDecoration : iconBigintToHash(avatarDecoration)
  }.png`;
}

/**
 * Builds a URL to a user's banner stored in the Discord CDN.
 *
 * @param userId - The ID of the user to get the banner of.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if no banner has not been set.
 */
export function bannerUrl(userId: BigString, options?: ImageOptions & { banner?: BigString }): string | undefined {
  return options?.banner
    ? formatImageUrl(
        `https://cdn.discordapp.com/banners/${userId}/${typeof options.banner === 'string' ? options.banner : iconBigintToHash(options.banner)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds a URL to the guild banner stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild to get the link to the banner for.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if no banner has been set.
 */
export function guildBannerUrl(guildId: BigString, options: ImageOptions & { banner?: BigString }): string | undefined {
  return options.banner
    ? formatImageUrl(
        `https://cdn.discordapp.com/banners/${guildId}/${typeof options.banner === 'string' ? options.banner : iconBigintToHash(options.banner)}`,
        options.size ?? 128,
        options.format,
      )
    : undefined;
}

/**
 * Builds a URL to the guild icon stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild to get the link to the banner for.
 * @param imageHash - The hash identifying the event cover image.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if no banner has been set.
 */
export function guildIconUrl(guildId: BigString, imageHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return imageHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/icons/${guildId}/${typeof imageHash === 'string' ? imageHash : iconBigintToHash(imageHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a guild splash stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild to get the splash of.
 * @param imageHash - The hash identifying the splash image.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if the guild does not have a splash image set.
 */
export function guildSplashUrl(guildId: BigString, imageHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return imageHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/splashes/${guildId}/${typeof imageHash === 'string' ? imageHash : iconBigintToHash(imageHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a guild discovery splash stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild to get the splash of.
 * @param imageHash - The hash identifying the discovery splash image.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if the guild does not have a splash image set.
 */
export function guildDiscoverySplashUrl(guildId: BigString, imageHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return imageHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/discovery-splashes/${guildId}/${typeof imageHash === 'string' ? imageHash : iconBigintToHash(imageHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a guild scheduled event cover stored in the Discord CDN.
 *
 * @param eventId - The ID of the scheduled event to get the cover of.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function guildScheduledEventCoverUrl(eventId: BigString, options: ImageOptions & { cover?: BigString }): string | undefined {
  return options.cover
    ? formatImageUrl(
        `https://cdn.discordapp.com/guild-events/${eventId}/${typeof options.cover === 'string' ? options.cover : iconBigintToHash(options.cover)}`,
        options.size ?? 128,
        options.format,
      )
    : undefined;
}

/**
 * Builds a URL to the guild widget image stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild to get the link to the widget image for.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource.
 */
export function getWidgetImageUrl(guildId: BigString, options?: GetGuildWidgetImageQuery): string {
  let url = `https://discordapp.com/api/guilds/${guildId}/widget.png`;

  if (options?.style) {
    url += `?style=${options.style}`;
  }

  return url;
}

/**
 * Builds a URL to a member's avatar stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild where the member is
 * @param userId - The ID of the user to get the avatar of.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if no banner has been set.
 */
export function memberAvatarUrl(guildId: BigString, userId: BigString, options?: ImageOptions & { avatar?: BigString }): string | undefined {
  return options?.avatar
    ? formatImageUrl(
        `https://cdn.discordapp.com/guilds/${guildId}/users/${userId}/avatars/${
          typeof options.avatar === 'string' ? options.avatar : iconBigintToHash(options.avatar)
        }`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds a URL to a member's banner stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild where the member is
 * @param userId - The ID of the user to get the banner of.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if no banner has been set.
 */
export function memberBannerUrl(guildId: BigString, userId: BigString, options?: ImageOptions & { banner?: BigString }): string | undefined {
  return options?.banner
    ? formatImageUrl(
        `https://cdn.discordapp.com/guilds/${guildId}/users/${userId}/banners/${
          typeof options.banner === 'string' ? options.banner : iconBigintToHash(options.banner)
        }`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to an application icon stored in the Discord CDN.
 *
 * @param applicationId - The ID of the application to get the icon of.
 * @param iconHash - The hash identifying the application icon.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`
 */
export function applicationIconUrl(applicationId: BigString, iconHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return iconHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/app-icons/${applicationId}/${typeof iconHash === 'string' ? iconHash : iconBigintToHash(iconHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to an application cover stored in the Discord CDN.
 *
 * @param applicationId - The ID of the application to get the cover of.
 * @param coverHash - The hash identifying the application cover.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function applicationCoverUrl(applicationId: BigString, coverHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return coverHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/app-icons/${applicationId}/${typeof coverHash === 'string' ? coverHash : iconBigintToHash(coverHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to an application asset stored in the Discord CDN.
 *
 * @param applicationId - The ID of the application to get the asset of.
 * @param assetId - The id identifying the application asset.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function applicationAssetUrl(applicationId: BigString, assetId: BigString | undefined, options?: ImageOptions): string | undefined {
  return assetId
    ? formatImageUrl(
        `https://cdn.discordapp.com/app-icons/${applicationId}/${typeof assetId === 'string' ? assetId : iconBigintToHash(assetId)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a sticker pack banner stored in the Discord CDN.
 *
 * @param bannerAssetId - The ID of the banner asset for the sticker pack.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function stickerPackBannerUrl(bannerAssetId: BigString | undefined, options?: ImageOptions): string | undefined {
  return bannerAssetId
    ? formatImageUrl(
        `https://cdn.discordapp.com/app-assets/710982414301790216/store/${
          typeof bannerAssetId === 'string' ? bannerAssetId : iconBigintToHash(bannerAssetId)
        }`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a sticker stored in the Discord CDN.
 *
 * @param stickerId - The ID of the sticker to get the icon of
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function stickerUrl(stickerId: BigString | number, options?: ImageOptions & { type?: StickerFormatTypes }): string | undefined {
  if (!stickerId) return;

  const url =
    options?.type === StickerFormatTypes.Gif
      ? `https://media.discordapp.net/stickers/${stickerId}`
      : `https://cdn.discordapp.com/stickers/${stickerId}`;

  return formatImageUrl(url, options?.size ?? 128, options?.format);
}

/**
 * Builds the URL to a team icon stored in the Discord CDN.
 *
 * @param teamId - The ID of the team to get the icon of
 * @param iconHash - The hash of the team icon.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function teamIconUrl(teamId: BigString, iconHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return iconHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/team-icons/${teamId}/store/${typeof iconHash === 'string' ? iconHash : iconBigintToHash(iconHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a role icon stored in the Discord CDN.
 *
 * @param roleId - The ID of the role to get the icon of
 * @param iconHash - The hash of the role icon.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined`.
 */
export function roleIconUrl(roleId: BigString, iconHash: BigString | undefined, options?: ImageOptions): string | undefined {
  return iconHash
    ? formatImageUrl(
        `https://cdn.discordapp.com/role-icons/${roleId}/${typeof iconHash === 'string' ? iconHash : iconBigintToHash(iconHash)}`,
        options?.size ?? 128,
        options?.format,
      )
    : undefined;
}

/**
 * Builds the URL to a guild tag badge stored in the Discord CDN.
 *
 * @param guildId - The ID of the guild to get the tag badge of
 * @param badgeHash - The hash identifying the guild tag badge.
 * @param options - The parameters for the building of the URL.
 * @returns The link to the resource or `undefined` if no badge has been set.
 */
export function guildTagBadgeUrl(guildId: BigString, badgeHash: BigString | undefined, options?: ImageOptions): string | undefined {
  if (badgeHash === undefined) return undefined;

  return formatImageUrl(`https://cdn.discordapp.com/guild-tag-badges/${guildId}/${badgeHash}`, options?.size ?? 128, options?.format);
}
