import { Jsonify } from "type-fest";

import { AttachmentView as SdkAttachmentView } from "@bitwarden/sdk-internal";

import { DECRYPT_ERROR, EncString } from "../../../key-management/crypto/models/enc-string";
import { View } from "../../../models/view/view";
import { SymmetricCryptoKey } from "../../../platform/models/domain/symmetric-crypto-key";
import { Attachment } from "../domain/attachment";

export class AttachmentView implements View {
  id?: string;
  url?: string;
  size?: string;
  sizeName?: string;
  fileName?: string;
  key?: SymmetricCryptoKey;
  /**
   * The SDK returns an encrypted key for the attachment.
   */
  encryptedKey: EncString | undefined;
  private _hasDecryptionError?: boolean;

  constructor(a?: Attachment) {
    if (!a) {
      return;
    }

    this.id = a.id;
    this.url = a.url;
    this.size = a.size;
    this.sizeName = a.sizeName;
  }

  get fileSize(): number {
    try {
      if (this.size != null) {
        return parseInt(this.size);
      }
    } catch {
      // Invalid file size.
    }
    return 0;
  }

  get hasDecryptionError(): boolean {
    return this._hasDecryptionError || this.fileName === DECRYPT_ERROR;
  }

  set hasDecryptionError(value: boolean) {
    this._hasDecryptionError = value;
  }

  static fromJSON(obj: Partial<Jsonify<AttachmentView>>): AttachmentView {
    const key = obj.key == null ? null : SymmetricCryptoKey.fromJSON(obj.key);

    let encryptedKey: EncString | undefined;
    if (obj.encryptedKey != null) {
      if (typeof obj.encryptedKey === "string") {
        // If the key is a string, we need to parse it as EncString
        encryptedKey = EncString.fromJSON(obj.encryptedKey);
      } else if ((obj.encryptedKey as any) instanceof EncString) {
        // If the key is already an EncString instance, we can use it directly
        encryptedKey = obj.encryptedKey;
      }
    }
    return Object.assign(new AttachmentView(), obj, { key: key, encryptedKey: encryptedKey });
  }

  /**
   * Converts the AttachmentView to a SDK AttachmentView.
   */
  toSdkAttachmentView(): SdkAttachmentView {
    return {
      id: this.id,
      url: this.url,
      size: this.size,
      sizeName: this.sizeName,
      fileName: this.fileName,
      key: this.encryptedKey?.toSdk(),
      // TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
      decryptedKey: this.key ? this.key.toBase64() : undefined,
    };
  }

  /**
   * Converts the SDK AttachmentView to a AttachmentView.
   */
  static fromSdkAttachmentView(
    obj: SdkAttachmentView,
    failure = false,
  ): AttachmentView | undefined {
    if (!obj) {
      return undefined;
    }

    const view = new AttachmentView();
    view.id = obj.id;
    view.url = obj.url;
    view.size = obj.size;
    view.sizeName = obj.sizeName;
    view.fileName = obj.fileName;
    // TODO: PM-23005 - Temporary field, should be removed when encrypted migration is complete
    view.key = obj.decryptedKey ? SymmetricCryptoKey.fromString(obj.decryptedKey) : undefined;
    view.encryptedKey = obj.key ? new EncString(obj.key) : undefined;
    view._hasDecryptionError = failure;

    return view;
  }

  /**
   * Determines if the attachment is a legacy attachment without a per-attachment key.
   * In this case, the attachment is encrypted with the user's user-key
   */
  isLegacyAttachment(): boolean {
    return this.key == null && this.encryptedKey == null;
  }
}
