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

import type { LinkPreviewForUIType } from './message/LinkPreviews.std.ts';
import type { MIMEType } from './MIME.std.ts';
import type {
  WithOptionalProperties,
  WithRequiredProperties,
} from './Util.std.ts';
import type { SignalService as Proto } from '../protobuf/index.std.ts';

export type ThumbnailType = EphemeralAttachmentFields & {
  size: number;
  contentType: MIMEType;
  path?: string;
  plaintextHash?: string;
  width?: number;
  height?: number;
  version?: 1 | 2;
  localKey?: string; // AES + MAC
};

export type ScreenshotType = WithOptionalProperties<ThumbnailType, 'size'>;
export type BackupThumbnailType = WithOptionalProperties<ThumbnailType, 'size'>;

// These fields do not get saved to the DB.
export type EphemeralAttachmentFields = {
  totalDownloaded?: number;
  data?: Uint8Array<ArrayBuffer>;
  /** Not included in protobuf, needs to be pulled from flags */
  isVoiceMessage?: boolean;
  /** For messages not already on disk, this will be a data url */
  url?: string;
  incrementalUrl?: string;
  screenshotData?: Uint8Array<ArrayBuffer>;
  /** @deprecated Legacy field */
  screenshotPath?: string;

  /** @deprecated Legacy field. Used only for downloading old attachment */
  id?: number;
  /** @deprecated Legacy field, used long ago for migrating attachments to disk. */
  schemaVersion?: number;
  /** @deprecated Legacy field, replaced by cdnKey */
  cdnId?: string;
  /** @deprecated Legacy fields, no longer needed */
  iv?: never;
  isReencryptableToSameDigest?: never;
  reencryptionInfo?: never;
};

/**
 * Adding a field to AttachmentType requires:
 * 1) adding a column to message_attachments
 * 2) updating MessageAttachmentDBReferenceType and MESSAGE_ATTACHMENT_COLUMNS
 * 3) saving data to the proper column
 */
export type AttachmentType = EphemeralAttachmentFields & {
  error?: boolean;
  blurHash?: string;
  caption?: string;
  clientUuid?: string;
  contentType: MIMEType;
  digest?: string;
  fileName?: string;
  plaintextHash?: string;
  uploadTimestamp?: number;
  size: number;
  duration?: number;
  pending?: boolean;
  width?: number;
  height?: number;
  path?: string;
  screenshot?: ScreenshotType;
  flags?: number;
  thumbnail?: ThumbnailType;
  isCorrupted?: boolean;
  cdnNumber?: number;
  cdnKey?: string;
  downloadPath?: string;
  key?: string;

  textAttachment?: TextAttachmentType;
  wasTooBig?: boolean;

  // If `true` backfill is unavailable
  backfillError?: boolean;

  incrementalMac?: string;
  chunkSize?: number;
  backupCdnNumber?: number;
  localBackupPath?: string;

  // See app/attachment_channel.ts
  version?: 1 | 2;
  localKey?: string; // AES + MAC
  thumbnailFromBackup?: BackupThumbnailType;

  /** For quote attachments, if copied from the referenced attachment */
  copied?: boolean;
};

export type LocalAttachmentV2Type = Readonly<{
  version: 2;
  path: string;
  localKey: string;
  plaintextHash: string;
  size: number;
}>;

export type AddressableAttachmentType = Readonly<{
  version?: 1 | 2;
  path: string;
  localKey?: string;
  size?: number;
  contentType: MIMEType;

  // In-memory data, for outgoing attachments that are not saved to disk.
  data?: Uint8Array<ArrayBuffer>;
}>;

export type AttachmentForUIType = AttachmentType & {
  isPermanentlyUndownloadable: boolean;
  thumbnailFromBackup?: {
    url?: string;
  };
};

export type UploadedAttachmentType = Omit<
  Proto.AttachmentPointer.Params,
  'attachmentIdentifier'
> &
  Readonly<{
    // Required fields
    attachmentIdentifier: Readonly<{
      cdnKey: string;
    }>;
    key: Uint8Array<ArrayBuffer>;
    size: number;
    digest: Uint8Array<ArrayBuffer>;
    contentType: string;
    plaintextHash: string;
  }>;

export type AttachmentWithHydratedData = AttachmentType & {
  data: Uint8Array<ArrayBuffer>;
};

export enum TextAttachmentStyleType {
  DEFAULT = 0,
  REGULAR = 1,
  BOLD = 2,
  SERIF = 3,
  SCRIPT = 4,
  CONDENSED = 5,
}

export type TextAttachmentType = {
  text?: string | null;
  textStyle?: number | null;
  textForegroundColor?: number | null;
  textBackgroundColor?: number | null;
  preview?: LinkPreviewForUIType;
  gradient?: {
    startColor?: number | null;
    endColor?: number | null;
    angle?: number | null;
    colors?: ReadonlyArray<number> | null;
    positions?: ReadonlyArray<number> | null;
  } | null;
  color?: number | null;
};

export type BaseAttachmentDraftType = {
  blurHash?: string;
  contentType: MIMEType;
  screenshotContentType?: MIMEType;
  size: number;
  flags?: number;
};

// An ephemeral attachment type, used between user's request to add the attachment as
//   a draft and final save on disk and in conversation.draftAttachments.
export type InMemoryAttachmentDraftType =
  | ({
      data: Uint8Array<ArrayBuffer>;
      clientUuid: string;
      pending: false;
      screenshotData?: Uint8Array<ArrayBuffer>;
      duration?: number;
      fileName?: string;
      path?: string;
    } & BaseAttachmentDraftType)
  | {
      contentType: MIMEType;
      clientUuid: string;
      fileName?: string;
      path?: string;
      pending: true;
      size: number;
      duration?: number;
    };

// What's stored in conversation.draftAttachments
export type AttachmentDraftType =
  | ({
      url?: string;
      screenshot?: ScreenshotType;
      // Legacy field
      screenshotPath?: string;
      pending: false;
      // Old draft attachments may have a caption, though they are no longer editable
      //   because we removed the caption editor.
      caption?: string;
      fileName?: string;
      path: string;
      width?: number;
      height?: number;
      clientUuid: string;
      version?: 2;
      localKey?: string;
    } & BaseAttachmentDraftType)
  | {
      clientUuid: string;
      contentType: MIMEType;
      fileName?: string;
      path?: string;
      pending: true;
      size: number;
    };

export enum AttachmentVariant {
  Default = 'Default',
  ThumbnailFromBackup = 'thumbnailFromBackup',
}

export type BackupableAttachmentType = WithRequiredProperties<
  AttachmentType,
  'plaintextHash' | 'key'
>;
export type AttachmentReadyForLocalBackup = WithRequiredProperties<
  AttachmentType,
  'plaintextHash' | 'localKey' | 'path'
>;

export type AttachmentDownloadableFromTransitTier = WithRequiredProperties<
  AttachmentType,
  'key' | 'digest' | 'cdnKey' | 'cdnNumber'
>;

export type LocallySavedAttachment = WithRequiredProperties<
  AttachmentType,
  'path'
>;

export class AttachmentSizeError extends Error {
  constructor(message: string) {
    super(`AttachmentSizeError: ${message}`);
  }
}
// oxlint-disable-next-line max-classes-per-file
export class AttachmentPermanentlyUndownloadableError extends Error {
  constructor(message: string) {
    super(`AttachmentPermanentlyUndownloadableError: ${message}`);
  }
}
