import { DecoratorNode, ElementNode, type LexicalNode, type SerializedElementNode, type SerializedLexicalNode, type Spread } from "lexical";
import { FocusableNode } from "./focusable_node";
import type { ReactNode } from "react";
import { LinkIcon } from "~/components/editor/link_icon";

type SerializedLinkNode = Spread<SerializedElementNode, {
    url: string;
}>;

export class LinkNode extends FocusableNode {
    __url: string;

    getUrl(): string {
        const self = this.getLatest();
        return self.__url;
    }

    static getType() {
        return "link";
    }
    static clone(node: LinkNode) {
        return new LinkNode(node.__url, node.__key, node.__hasFocus);
    }
    constructor(url: string, key?: string, hasFocus?: boolean) {
        super(key, hasFocus);
        this.__url = url;
    }

    isInline(): boolean {
        return true;
    }
    createDOM(): HTMLElement {
        const element = super.createDOM();
        element.classList.add("link");
        return element;
    }
    updateDOM(old: LinkNode, dom: HTMLElement): boolean {
        return super.updateDOM(old, dom);
    }
    exportJSON(): SerializedLinkNode {
        return {
            ...super.exportJSON(),
            type: "link",
            url: this.__url,
        };
    }
    static importJSON(serializedNode: SerializedLinkNode) {
        return $createLinkNode(serializedNode.url);
    }
}

export function $createLinkNode(url: string) {
    return new LinkNode(url);
}

export function $isLinkNode(node?: LexicalNode | null): node is LinkNode {
    return node instanceof LinkNode;
}

export class LinkMarkerNode extends ElementNode {
    static getType() {
        return "link-marker";
    }
    static clone(node: LinkMarkerNode) {
        return new LinkMarkerNode(node.__key);
    }
    isInline(): boolean {
        return true;
    }
    createDOM(): HTMLElement {
        const element = document.createElement("span");
        element.classList.add("link-marker");
        return element;
    }
    updateDOM(): boolean {
        return false;
    }
    exportJSON(): SerializedElementNode {
        return {
            ...super.exportJSON(),
            type: "link-marker",
        };
    }
    static importJSON() {
        return $createLinkMarkerNode();
    }
}

export function $createLinkMarkerNode() {
    return new LinkMarkerNode();
}

export function $isLinkMarkerNode(node?: LexicalNode | null): node is LinkMarkerNode {
    return node instanceof LinkMarkerNode;
}

export class LinkUrlNode extends ElementNode {
    static getType() {
        return "link-url";
    }
    static clone(node: LinkUrlNode) {
        return new LinkUrlNode(node.__key);
    }
    isInline(): boolean {
        return true;
    }
    createDOM(): HTMLElement {
        const element = document.createElement("span");
        element.classList.add("link-url");
        return element;
    }
    updateDOM(): boolean {
        return false;
    }
    exportJSON(): SerializedElementNode {
        return {
            ...super.exportJSON(),
            type: "link-url",
        };
    }
    static importJSON() {
        return $createLinkUrlNode();
    }
}

export function $createLinkUrlNode() {
    return new LinkUrlNode();
}

export function $isLinkUrlNode(node?: LexicalNode | null): node is LinkUrlNode {
    return node instanceof LinkUrlNode;
}

type SerializedLinkIconNode = Spread<SerializedLexicalNode, {
    url: string;
}>;

export class LinkIconNode extends DecoratorNode<ReactNode> {
    __url: string;

    getUrl() {
        const self = this.getLatest();
        return self.__url;
    }

    static getType() {
        return "link-icon";
    }
    static clone(node: LinkIconNode) {
        return new LinkIconNode(node.__url, node.__key);
    }
    constructor(url: string, key?: string) {
        super(key);
        this.__url = url;
    }
    createDOM(): HTMLElement {
        return document.createElement("span");
    }
    updateDOM(): boolean {
        return false;
    }
    decorate(): ReactNode {
        return (<LinkIcon url={this.__url} />);
    }
    exportJSON(): SerializedLinkIconNode {
        return {
            ...super.exportJSON(),
            type: "link-icon",
            url: this.__url,
        }
    }
    static importJSON(serializedNode: SerializedLinkIconNode) {
        return $createLinkIconNode(serializedNode.url);
    }
}

export function $createLinkIconNode(url: string) {
    return new LinkIconNode(url);
}

export function $isLinkIconNode(node?: LexicalNode | null): node is LinkIconNode {
    return node instanceof LinkIconNode;
}

