// Copyright 2021 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only

import * as z from 'zod';
import { isEmpty } from 'lodash';
import { isRecord } from '../util/isRecord';
import { isNormalNumber } from '../util/isNormalNumber';
import * as log from '../logging/log';
import type { BadgeType, BadgeImageType } from './types';
import { parseBadgeCategory } from './BadgeCategory';
import { BadgeImageTheme, parseBadgeImageTheme } from './BadgeImageTheme';

const MAX_BADGES = 1000;

const badgeFromServerSchema = z.object({
  category: z.string(),
  description: z.string(),
  id: z.string(),
  name: z.string(),
  svg: z.string(),
  svgs: z.array(z.record(z.string())).length(3),
  expiration: z.number().optional(),
  visible: z.boolean().optional(),
});

export function parseBadgesFromServer(
  value: unknown,
  updatesUrl: string
): Array<BadgeType> {
  if (!Array.isArray(value)) {
    return [];
  }

  const result: Array<BadgeType> = [];

  const numberOfBadgesToParse = Math.min(value.length, MAX_BADGES);
  for (let i = 0; i < numberOfBadgesToParse; i += 1) {
    const item = value[i];

    const parseResult = badgeFromServerSchema.safeParse(item);
    if (!parseResult.success) {
      log.warn(
        'parseBadgesFromServer got an invalid item',
        parseResult.error.format()
      );
      continue;
    }

    const {
      category,
      description: descriptionTemplate,
      expiration,
      id,
      name,
      svg,
      svgs,
      visible,
    } = parseResult.data;
    const images = parseImages(svgs, svg, updatesUrl);
    if (images.length !== 4) {
      log.warn('Got invalid number of SVGs from the server');
      continue;
    }

    result.push({
      id,
      category: parseBadgeCategory(category),
      name,
      descriptionTemplate,
      images,
      ...(isNormalNumber(expiration) && typeof visible === 'boolean'
        ? {
            expiresAt: expiration * 1000,
            isVisible: visible,
          }
        : {}),
    });
  }

  return result;
}

const parseImages = (
  rawSvgs: ReadonlyArray<Record<string, string>>,
  rawSvg: string,
  updatesUrl: string
): Array<BadgeImageType> => {
  const result: Array<BadgeImageType> = [];

  for (const item of rawSvgs) {
    if (!isRecord(item)) {
      log.warn('Got invalid SVG from the server');
      continue;
    }

    const image: BadgeImageType = {};
    for (const [rawTheme, filename] of Object.entries(item)) {
      if (typeof filename !== 'string') {
        log.warn('Got an SVG from the server that lacked a valid filename');
        continue;
      }
      const theme = parseBadgeImageTheme(rawTheme);
      image[theme] = { url: parseImageFilename(filename, updatesUrl) };
    }

    if (isEmpty(image)) {
      log.warn('Got an SVG from the server that lacked valid values');
    } else {
      result.push(image);
    }
  }

  result.push({
    [BadgeImageTheme.Transparent]: {
      url: parseImageFilename(rawSvg, updatesUrl),
    },
  });

  return result;
};

const parseImageFilename = (filename: string, updatesUrl: string): string =>
  new URL(`/static/badges/${filename}`, updatesUrl).toString();
