export enum ImageSize {
  USER_AVATAR = 32,
  USER_DECORATION = 64,
  SERVER_TAG = 32,
  ACTIVITY_LARGE = 128,
  ACTIVITY_SMALL = 64,
  EMOJI = 32,
}

export type Activity = {
  name?: string;
  type?: number;
  details?: string | null;
  url?: string | null;
  application_id?: string | null;
  assets?: { large_image?: string | null; small_image?: string | null };
  emoji?: { id?: string; animated?: boolean } | null;
};

export type Data = {
  discord_user: {
    id: string;
    avatar?: string | null;
    discriminator?: string | null;
    primary_guild?: { identity_guild_id?: string; badge?: string } | null;
    avatar_decoration_data?: { asset?: string } | null;
  };
  activities: Activity[];
  spotify?: { album_art_url?: string } | null;
};

export type ProfileSettings = {
  optimized?: boolean;
  animatedDecoration?: string | boolean;
  ignoreAppId?: string[];
  theme?: string | null;
};

async function encodeBase64(url: string, _size?: ImageSize, _theme?: string | null) {
  try {
    const res = await fetch(url);
    if (!res.ok) return url;
    const buf = await res.arrayBuffer();
    const bytes = new Uint8Array(buf);
    let binary = "";
    const chunk = 0x8000;
    for (let i = 0; i < bytes.length; i += chunk) {
      binary += String.fromCharCode.apply(null, Array.from(bytes.subarray(i, i + chunk)) as unknown as number[]);
    }
    const b64 = typeof globalThis.btoa === "function" ? globalThis.btoa(binary) : Buffer.from(binary, "binary").toString("base64");
    // try to infer mime
    const ext = url.split("?")[0].split(".").pop()?.toLowerCase() ?? "png";
    const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg" : ext === "gif" ? "image/gif" : ext === "webp" ? "image/webp" : "image/png";
    return `data:${mime};base64,${b64}`;
  } catch (err) {
    return url;
  }
}

export async function fetchUserImages(data: Data, settings: ProfileSettings) {
  let avatar: string;
  let avatarDecoration: string | null = null;
  let clanBadge: string | null = null;
  let assetLargeImage: string | null = null;
  let assetSmallImage: string | null = null;
  let userEmoji: string | null = null;
  let albumCover: string | null = null;

  const avatarExtension =
    data.discord_user.avatar && data.discord_user.avatar.startsWith("a_") && !settings.optimized
      ? "gif"
      : "webp";

  const statusExtension: string = data.activities[0]?.emoji?.animated && !settings.optimized ? "gif" : "webp";

  const userStatus: Activity | undefined = data.activities[0] && data.activities[0].type === 4 ? data.activities[0] : undefined;

  const activities = data.activities
    .filter((activity) => activity.type === 0)
    .filter((activity) => !settings.ignoreAppId?.includes(activity.application_id ?? ""));
  const activity: Activity | undefined = activities.length > 0 ? activities[0] : undefined;

  if (data.discord_user.avatar) {
    avatar = await encodeBase64(
      `https://cdn.discordapp.com/avatars/${data.discord_user.id}/${data.discord_user.avatar}.${avatarExtension}?size=${avatarExtension === "gif" ? "64" : "256"}`,
      ImageSize.USER_AVATAR
    );
  } else {
    avatar = await encodeBase64(
      `https://cdn.discordapp.com/embed/avatars/${data.discord_user.discriminator === "0" ? Number(BigInt(data.discord_user.id) >> BigInt(22)) % 6 : Number(data.discord_user.discriminator ?? "0") % 5}.png?size=128`,
      ImageSize.USER_AVATAR
    );
  }

  if (data.discord_user.primary_guild && data.discord_user.primary_guild.identity_guild_id && data.discord_user.primary_guild.badge) {
    clanBadge = await encodeBase64(
      `https://cdn.discordapp.com/clan-badges/${data.discord_user.primary_guild.identity_guild_id}/${data.discord_user.primary_guild.badge}.png?size=32`,
      ImageSize.SERVER_TAG
    );
  }

  if (data.discord_user.avatar_decoration_data?.asset) {
    avatarDecoration = await encodeBase64(
      `https://cdn.discordapp.com/avatar-decoration-presets/${data.discord_user.avatar_decoration_data.asset}.png?size=64&passthrough=${settings.animatedDecoration || "false"}`,
      ImageSize.USER_DECORATION
    );
  }

  if (activity?.assets?.large_image)
    assetLargeImage = await encodeBase64(
      activity.assets?.large_image!.startsWith("mp:external/")
        ? `https://media.discordapp.net/${activity.assets!.large_image!.replace("mp:", "")}`
        : `https://cdn.discordapp.com/app-assets/${activity.application_id}/${activity.assets!.large_image}.webp`,
      ImageSize.ACTIVITY_LARGE,
      settings.theme || null
    );

  if (activity?.assets?.small_image)
    assetSmallImage = await encodeBase64(
      activity.assets.small_image!.startsWith("mp:external/")
        ? `https://media.discordapp.net/${activity.assets.small_image!.replace("mp:", "")}`
        : `https://cdn.discordapp.com/app-assets/${activity.application_id}/${activity.assets.small_image}.webp`,
      ImageSize.ACTIVITY_SMALL,
      settings.theme || null
    );

  if (userStatus?.emoji?.id)
    userEmoji = await encodeBase64(
      `https://cdn.discordapp.com/emojis/${userStatus.emoji.id}.${statusExtension}?size=32`,
      ImageSize.EMOJI
    );

  if (data.spotify?.album_art_url) albumCover = await encodeBase64(data.spotify.album_art_url, ImageSize.ACTIVITY_LARGE);

  return {
    avatar,
    clanBadge,
    avatarDecoration,
    assetLargeImage,
    assetSmallImage,
    userEmoji,
    albumCover,
  };
}

export default fetchUserImages;
