/* eslint-disable jsx-a11y/no-static-element-interactions */
/* eslint-disable jsx-a11y/click-events-have-key-events */
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { action, computed, makeObservable, observable, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { StopEvent, returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc } from '../../../fields/Doc';
import { DocData } from '../../../fields/DocSymbols';
import { StrCast } from '../../../fields/Types';
import { DocUtils } from '../../documents/DocUtils';
import { DragManager } from '../../util/DragManager';
import { Hypothesis } from '../../util/HypothesisUtils';
import { LinkManager } from '../../util/LinkManager';
import { UndoManager, undoBatch } from '../../util/UndoManager';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { PinProps } from '../PinFuncs';
import './DocumentLinksButton.scss';
import { DocumentView } from './DocumentView';
import { LinkDescriptionPopup } from './LinkDescriptionPopup';
import { TaskCompletionBox } from './TaskCompletedBox';

interface DocumentLinksButtonProps {
    View: DocumentView;
    Bottom?: boolean;
    AlwaysOn?: boolean;
    InMenu?: boolean;
    OnHover?: boolean;
    StartLink?: boolean; // whether the link HAS been started (i.e. now needs to be completed)
    ShowCount?: boolean;
    scaling?: () => number; // how uch doc is scaled so that link buttons can invert it
    hideCount?: () => boolean;
}

export class DocButtonState {
    @observable public StartLink: Doc | undefined = undefined; // origin's Doc, if defined
    @observable public StartLinkView: DocumentView | undefined = undefined;
    @observable public AnnotationId: string | undefined = undefined;
    @observable public AnnotationUri: string | undefined = undefined;
    @observable public LinkEditorDocView: DocumentView | undefined = undefined;
    // eslint-disable-next-line no-use-before-define
    public static _instance: DocButtonState | undefined;
    public static get Instance() {
        // eslint-disable-next-line no-return-assign
        return DocButtonState._instance ?? (DocButtonState._instance = new DocButtonState());
    }
    constructor() {
        makeObservable(this);
    }
}
@observer
export class DocumentLinksButton extends ObservableReactComponent<DocumentLinksButtonProps> {
    private _linkButton = React.createRef<HTMLDivElement>();
    public static get StartLink() { return DocButtonState.Instance.StartLink; } // prettier-ignore
    public static set StartLink(value) { runInAction(() => {DocButtonState.Instance.StartLink = value}); } // prettier-ignore
    @observable public static StartLinkView: DocumentView | undefined = undefined;
    @observable public static AnnotationId: string | undefined = undefined;
    @observable public static AnnotationUri: string | undefined = undefined;
    constructor(props: any) {
        super(props);
        makeObservable(this);
    }

    @undoBatch
    onLinkButtonMoved = (e: PointerEvent) => {
        if (this._props.InMenu && this._props.StartLink) {
            if (this._linkButton.current !== null) {
                const linkDrag = UndoManager.StartBatch('Drag Link');
                this._props.View &&
                    DragManager.StartLinkDrag(this._linkButton.current, this._props.View, this._props.View.ComponentView?.getAnchor, e.pageX, e.pageY, {
                        dragComplete: dropEv => {
                            if (this._props.View && dropEv.linkDocument) {
                                // dropEv.linkDocument equivalent to !dropEve.aborted since linkDocument is only assigned on a completed drop
                                !dropEv.linkDocument.link_relationship && (Doc.GetProto(dropEv.linkDocument).link_relationship = 'hyperlink');
                            }
                            linkDrag?.end();
                        },
                        hideSource: false,
                    });
                return true;
            }
            return false;
        }
        return false;
    };

    onLinkMenuOpen = (e: React.PointerEvent): void => {
        setupMoveUpEvents(
            this,
            e,
            this.onLinkButtonMoved,
            emptyFunction,
            action((clickEv, doubleTap) => {
                doubleTap && DocumentView.showBackLinks(this._props.View.Document);
            }),
            undefined,
            undefined,
            action(() => {
                DocButtonState.Instance.LinkEditorDocView = this._props.View;
            })
        );
    };

    onLinkButtonDown = (e: React.PointerEvent): void => {
        setupMoveUpEvents(
            this,
            e,
            this.onLinkButtonMoved,
            emptyFunction,
            action((clickEv, doubleTap) => {
                if (doubleTap && this._props.InMenu && this._props.StartLink) {
                    // action(() => Doc.BrushDoc(this._props.View.Document));
                    if (DocumentLinksButton.StartLink === this._props.View.Document) {
                        DocumentLinksButton.StartLink = undefined;
                        DocumentLinksButton.StartLinkView = undefined;
                    } else {
                        DocumentLinksButton.StartLink = this._props.View.Document;
                        DocumentLinksButton.StartLinkView = this._props.View;
                    }
                }
            })
        );
    };

    @undoBatch
    onLinkClick = (): void => {
        if (this._props.InMenu && this._props.StartLink) {
            DocumentLinksButton.AnnotationId = undefined;
            DocumentLinksButton.AnnotationUri = undefined;
            if (DocumentLinksButton.StartLink === this._props.View.Document) {
                DocumentLinksButton.StartLink = undefined;
                DocumentLinksButton.StartLinkView = undefined;
            } else {
                // if this LinkButton's Document is undefined
                DocumentLinksButton.StartLink = this._props.View.Document;
                DocumentLinksButton.StartLinkView = this._props.View;
            }
        }
    };

    completeLink = (e: React.PointerEvent): void => {
        setupMoveUpEvents(
            this,
            e,
            returnFalse,
            emptyFunction,
            action(clickEv => DocumentLinksButton.finishLinkClick(clickEv.clientX, clickEv.clientY, DocumentLinksButton.StartLink, this._props.View.Document, true, this._props.View))
        );
    };

    @undoBatch
    public static finishLinkClick(screenX: number, screenY: number, startLink: Doc | undefined, endLink: Doc, startIsAnnotation: boolean, endLinkView?: DocumentView, pinProps?: PinProps) {
        runInAction(() => {
            if (startLink === endLink || !startLink) {
                DocumentLinksButton.StartLink = undefined;
                DocumentLinksButton.StartLinkView = undefined;
                DocumentLinksButton.AnnotationId = undefined;
                DocumentLinksButton.AnnotationUri = undefined;
                // !this._props.StartLink
            } else if (startLink !== endLink) {
                // eslint-disable-next-line no-param-reassign
                endLink = endLinkView?.ComponentView?.getAnchor?.(true, pinProps) || endLink;
                // eslint-disable-next-line no-param-reassign
                startLink = DocumentLinksButton.StartLinkView?.ComponentView?.getAnchor?.(true) || startLink;
                const linkDoc = DocUtils.MakeLink(startLink, endLink, { link_relationship: DocumentLinksButton.AnnotationId ? 'hypothes.is annotation' : undefined });

                LinkManager.Instance.currentLink = linkDoc;

                if (linkDoc) {
                    if (DocumentLinksButton.AnnotationId && DocumentLinksButton.AnnotationUri) {
                        // if linking from a Hypothes.is annotation
                        const linkDocData = linkDoc[DocData];
                        linkDocData.linksToAnnotation = true;
                        linkDocData.annotationId = DocumentLinksButton.AnnotationId;
                        linkDocData.annotationUri = DocumentLinksButton.AnnotationUri;
                        const dashHyperlink = Doc.globalServerPath(startIsAnnotation ? endLink : startLink);
                        Hypothesis.makeLink(StrCast(startIsAnnotation ? endLink.title : startLink.title), dashHyperlink, DocumentLinksButton.AnnotationId, startIsAnnotation ? startLink : endLink); // edit annotation to add a Dash hyperlink to the linked doc
                    }

                    TaskCompletionBox.textDisplayed = 'Link Created';
                    TaskCompletionBox.popupX = screenX;
                    TaskCompletionBox.popupY = screenY - 133;
                    TaskCompletionBox.taskCompleted = true;

                    if (LinkDescriptionPopup.Instance.showDescriptions === 'ON' || !LinkDescriptionPopup.Instance.showDescriptions) {
                        LinkDescriptionPopup.Instance.popupX = screenX;
                        LinkDescriptionPopup.Instance.popupY = screenY - 100;
                        LinkDescriptionPopup.Instance.display = true;
                    }

                    const rect = document.body.getBoundingClientRect();
                    if (LinkDescriptionPopup.Instance.popupX + 200 > rect.width) {
                        LinkDescriptionPopup.Instance.popupX -= 190;
                        TaskCompletionBox.popupX -= 40;
                    }
                    if (LinkDescriptionPopup.Instance.popupY + 100 > rect.height) {
                        LinkDescriptionPopup.Instance.popupY -= 40;
                        TaskCompletionBox.popupY -= 40;
                    }

                    setTimeout(
                        action(() => {
                            TaskCompletionBox.taskCompleted = false;
                        }),
                        2500
                    );
                }
            }
        });
    }

    @action clearLinks() {
        DocumentLinksButton.StartLink = undefined;
        DocumentLinksButton.StartLinkView = undefined;
    }

    @computed get filteredLinks() {
        const results = [] as Doc[];
        const filters = this._props.View._props.childFilters();
        Array.from(new Set<Doc>(this._props.View.allLinks)).forEach(link => {
            if (DocUtils.FilterDocs([link], filters, []).length || DocUtils.FilterDocs([link.link_anchor_2 as Doc], filters, []).length || DocUtils.FilterDocs([link.link_anchor_1 as Doc], filters, []).length) {
                results.push(link);
            }
        });
        return results;
    }

    /**
     * gets the JSX of the link button (btn used to start/complete links) OR the link-view button (btn on bottom left of each linked node)
     *
     * todo:glr / anh seperate functionality such as onClick onPointerDown of link menu button
     */
    @computed get linkButtonInner() {
        const btnDim = 30;
        const isActive = DocumentLinksButton.StartLink === this._props.View.Document && this._props.StartLink;
        const scaling = Math.min(1, this._props.scaling?.() || 1);
        const showLinkCount = (onHover?: boolean, offset?: boolean) => (
            <div
                className="documentLinksButton-showCount"
                onPointerDown={this.onLinkMenuOpen}
                style={{
                    fontSize: (onHover ? btnDim / 2 : 20) * scaling,
                    width: (onHover ? btnDim / 2 : btnDim) * scaling,
                    height: (onHover ? btnDim / 2 : btnDim) * scaling,
                    bottom: offset ? 5 * scaling : onHover ? (-btnDim / 2) * scaling : undefined,
                }}>
                <span style={{ width: '100%', display: 'inline-block', textAlign: 'center' }}>{Array.from(this.filteredLinks).length}</span>
            </div>
        );
        return this._props.ShowCount ? (
            showLinkCount(this._props.OnHover, this._props.Bottom)
        ) : (
            <div className="documentLinksButton-menu">
                {this._props.StartLink ? ( // if link has been started from current node, then set behavior of link button to deactivate linking when clicked again
                    <div className={`documentLinksButton ${isActive ? `startLink` : ``}`} ref={this._linkButton} onPointerDown={isActive ? StopEvent : this.onLinkButtonDown} onClick={isActive ? this.clearLinks : this.onLinkClick}>
                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                    </div>
                ) : null}
                {!this._props.StartLink && DocumentLinksButton.StartLink !== this._props.View.Document ? ( // if the origin node is not this node
                    <div className="documentLinksButton-endLink" ref={this._linkButton} onPointerDown={DocumentLinksButton.StartLink && this.completeLink}>
                        <FontAwesomeIcon className="documentdecorations-icon" icon="link" />
                    </div>
                ) : null}
            </div>
        );
    }

    render() {
        if (this.props.hideCount?.()) return null;
        const menuTitle = this._props.StartLink ? 'Drag or tap to start link' : 'Tap to complete link';
        const buttonTitle = 'Tap to view links; double tap to open link collection';
        const title = this._props.ShowCount ? buttonTitle : menuTitle;

        // render circular tooltip if it isn't set to invisible and show the number of doc links the node has, and render inner-menu link button for starting/stopping links if currently in menu
        return !Array.from(this.filteredLinks).length && !this._props.AlwaysOn ? null : (
            <div
                className="documentLinksButton-wrapper"
                style={{
                    position: this._props.InMenu ? 'relative' : 'absolute',
                    top: 0,
                    pointerEvents: 'none',
                }}>
                {DocButtonState.Instance.LinkEditorDocView ? this.linkButtonInner : <Tooltip title={<div className="dash-tooltip">{title}</div>}>{this.linkButtonInner}</Tooltip>}
            </div>
        );
    }
}
