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

import lodash from 'lodash';
import { strictAssert } from './assert.std.ts';

import type { AttachmentType } from '../types/Attachment.std.ts';

const { isNumber } = lodash;

export enum AttachmentDisposition {
  Attachment = 'attachment',
  AvatarData = 'avatarData',
  Draft = 'draft',
  Download = 'download',
  Sticker = 'sticker',
  Temporary = 'temporary',
}

export type GetLocalAttachmentUrlOptionsType = Readonly<{
  disposition?: AttachmentDisposition;
}>;

export function getLocalAttachmentUrl(
  attachment: Partial<
    Pick<
      AttachmentType,
      | 'contentType'
      | 'digest'
      | 'plaintextHash'
      | 'downloadPath'
      | 'incrementalMac'
      | 'chunkSize'
      | 'key'
      | 'localKey'
      | 'path'
      | 'size'
      | 'version'
      | 'copied'
    >
  >,
  {
    disposition = AttachmentDisposition.Attachment,
  }: GetLocalAttachmentUrlOptionsType = {}
): string {
  let { path } = attachment;

  if (disposition === AttachmentDisposition.Download) {
    strictAssert(
      attachment.incrementalMac && attachment.chunkSize,
      'To view downloads, must have incrementalMac/chunkSize'
    );
    path = attachment.downloadPath;
  }

  strictAssert(path != null, `${disposition} attachment was missing path`);

  // Fix Windows paths
  path = path.replace(/\\/g, '/');

  let url: URL;
  if (disposition === AttachmentDisposition.Download) {
    url = new URL(`attachment://v2/${path}`);
  } else if (attachment.version !== 2) {
    url = new URL(`attachment://v1/${path}`);
  } else {
    url = new URL(`attachment://v${attachment.version}/${path}`);
    if (attachment.localKey != null) {
      url.searchParams.set('key', attachment.localKey);
    }
  }

  if (attachment.size != null) {
    url.searchParams.set('size', attachment.size.toString());
  }

  if (attachment.contentType != null) {
    url.searchParams.set('contentType', attachment.contentType);
  }

  if (disposition !== AttachmentDisposition.Attachment) {
    url.searchParams.set('disposition', disposition);
  }

  if (disposition === AttachmentDisposition.Download) {
    if (!attachment.key) {
      throw new Error('getLocalAttachmentUrl: Missing attachment key!');
    }
    url.searchParams.set('key', attachment.key);

    // Attachments restored from backup / link & sync may have plaintextHash but not
    // digest
    if (attachment.plaintextHash) {
      url.searchParams.set('plaintextHash', attachment.plaintextHash);
    } else if (attachment.digest) {
      url.searchParams.set('digest', attachment.digest);
    } else {
      throw new Error(
        'getLocalAttachmentUrl: Missing attachment plaintextHash and digest!'
      );
    }

    if (!attachment.incrementalMac) {
      throw new Error(
        'getLocalAttachmentUrl: Missing attachment incrementalMac!'
      );
    }
    url.searchParams.set('incrementalMac', attachment.incrementalMac);

    if (!isNumber(attachment.chunkSize)) {
      throw new Error(
        'getLocalAttachmentUrl: Missing attachment incrementalMac!'
      );
    }
    url.searchParams.set('chunkSize', attachment.chunkSize.toString());
  }

  // For weak references (e.g. copied quotes) don't error if path is missing
  if (attachment.copied) {
    url.searchParams.set('weakReference', '1');
  }

  return url.toString();
}
