import { type } from "@heydovetail/assert";
import { NodeSpec } from "prosemirror-model";
import { AttrsSpec } from "../types";
import { AttachmentAttrs } from "./schema";

export type TypeName = "at";

export interface AttachmentAttrs {
  /**
   * "File Name"
   *
   * The name of the attachment's underlying file.
   */
  fn?: string;
  /**
   * "ID"
   *
   * The backend storage ID for the attachment.
   */
  id: string;
  /**
   * "Hide Preview"
   *
   * If `true`, indicates that no preview for the attachment should be
   * displayed, instead being displayed as a simple card.
   */
  hp?: boolean;
  /**
   * "Natural Height"
   *
   * For image attachments, the height dimension (in display pixels). For
   * high-DPI images the value is in display dimensions (not bitmap dimensions).
   * For example given an `image@2x.jpg` file with size 120×30, the height is 15.
   */
  nh?: number;
  /**
   * "Natural Width"
   *
   * For image attachments, the width dimension (in display pixels). For
   * high-DPI images the value is in display dimensions (not bitmap dimensions).
   * For example given an `image@2x.jpg` file with size 120×30, the width is 60.
   */
  nw?: number;
  /**
   * "Type"
   *
   * The type of attachment. This is used to choose an appropriate attachment
   * previewer. It's also used to determine the size of a placeholder based on
   * the natural size.
   */
  t?: string;
}

export const typeName: TypeName = "at";
export const domDataAttrType = "data-dovetail-type";
export const domDataAttrAttrs = "data-dovetail-attrs";

export const nodeSpec: NodeSpec = {
  attrs: type<AttrsSpec<AttachmentAttrs>>({
    fn: { default: undefined },
    id: {},
    hp: { default: undefined },
    nh: { default: undefined },
    nw: { default: undefined },
    t: { default: undefined }
  }),
  draggable: true,
  group: "block",
  parseDOM: [
    {
      tag: `div[${domDataAttrType}=${typeName}][${domDataAttrAttrs}]`,
      getAttrs: dom => {
        const element = dom as Element;

        // Validate attrs before accepting them. They could come from anywhere,
        // and be invalid (e.g. from a different version of the editor, or from
        // a name-clash).
        const attrsJson = element.getAttribute(domDataAttrAttrs);
        if (attrsJson !== null) {
          try {
            const { fn, id, hp, nh, nw, t } = JSON.parse(attrsJson) as {
              [P in keyof AttachmentAttrs]?: {} | null | string | number | boolean
            };
            if (typeof id === "string") {
              return {
                id,
                ...(typeof fn === "string" ? { fn } : null),
                ...(typeof hp === "boolean" ? { hp } : null),
                ...(typeof nh === "number" && typeof nw === "number" ? { nh, nw } : null),
                ...(typeof t === "string" ? { t } : null)
              } as AttachmentAttrs;
            }
          } catch {}
        }
        return false;
      }
    }
  ],
  toDOM(node) {
    return ["div", { [domDataAttrType]: typeName, [domDataAttrAttrs]: JSON.stringify(node.attrs) }];
  }
};
