/* eslint-disable jsx-a11y/no-static-element-interactions */
import { action, computed, IReactionDisposer, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import { NodeSelection } from 'prosemirror-state';
import * as React from 'react';
import * as ReactDOM from 'react-dom/client';
import { ClientUtils, returnFalse } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
import { Height, Width } from '../../../../fields/DocSymbols';
import { NumCast } from '../../../../fields/Types';
import { DocServer } from '../../../DocServer';
import { Docs, DocUtils } from '../../../documents/Documents';
import { Transform } from '../../../util/Transform';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { DocumentView } from '../DocumentView';
import { FocusViewOptions } from '../FieldView';
import { FormattedTextBox } from './FormattedTextBox';

const horizPadding = 3; // horizontal padding to container to allow cursor to show up on either side.
interface IDashDocViewInternal {
    docId: string;
    embedding: string;
    tbox: FormattedTextBox;
    width: string;
    height: string;
    hidden: boolean;
    fieldKey: string;
    view: any;
    node: any;
    getPos: any;
}

@observer
export class DashDocViewInternal extends ObservableReactComponent<IDashDocViewInternal> {
    _spanRef = React.createRef<HTMLDivElement>();
    _disposers: { [name: string]: IReactionDisposer } = {};
    _textBox: FormattedTextBox;
    @observable _dashDoc: Doc | undefined = undefined;
    @computed get _width() {
        return NumCast(this._dashDoc?._width);
    }
    @computed get _height() {
        return NumCast(this._dashDoc?._height);
    }

    updateDoc = action((dashDoc: Doc) => {
        this._dashDoc = dashDoc;

        if (this._props.width !== (this._dashDoc?._width ?? '') + 'px' || this._props.height !== (this._dashDoc?._height ?? '') + 'px') {
            try {
                // bcz: an exception will be thrown if two embeddings are open at the same time when a doc view comment is made
                this._props.view.dispatch(
                    this._props.view.state.tr.setNodeMarkup(this._props.getPos(), null, {
                        ...this._props.node.attrs,
                        width: this._width + 'px',
                        height: this._height + 'px',
                    })
                );
            } catch (e) {
                console.log('DashDocView:' + e);
            }
        }
    });

    constructor(props: IDashDocViewInternal) {
        super(props);
        makeObservable(this);
        this._textBox = this._props.tbox;

        DocServer.GetRefField(this._props.docId + this._props.embedding).then(async dashDoc => {
            if (!(dashDoc instanceof Doc)) {
                this._props.embedding &&
                    DocServer.GetRefField(this._props.docId).then(async dashDocBase => {
                        if (dashDocBase instanceof Doc) {
                            const embedding = Doc.MakeEmbedding(dashDocBase, this._props.docId + this._props.embedding);
                            embedding.layout_fieldKey = 'layout';
                            this._props.fieldKey && DocUtils.makeCustomViewClicked(embedding, Docs.Create.StackingDocument, this._props.fieldKey, undefined);
                            this.updateDoc(embedding);
                        }
                    });
            } else {
                this.updateDoc(dashDoc);
            }
        });
    }

    componentDidMount() {
        this._disposers.upater = reaction(
            () => ({ width: this._width, height: this._height, parent: this._spanRef.current?.parentNode as HTMLElement }),
            action(({ width, height, parent }) => {
                if (parent) {
                    parent.style.width = width + 'px';
                    parent.style.height = height + 'px';
                }
            })
        );
    }

    removeDoc = () => {
        this._props.view.dispatch(this._props.view.state.tr.setSelection(new NodeSelection(this._props.view.state.doc.resolve(this._props.getPos()))).deleteSelection());
        return true;
    };

    getDocTransform = () => {
        if (!this._spanRef.current) return Transform.Identity();
        const { scale, translateX, translateY } = ClientUtils.GetScreenTransform(this._spanRef.current);
        return new Transform(-translateX, -translateY, 1).scale(1 / scale);
    };
    outerFocus = (target: Doc, options: FocusViewOptions) => this._textBox.focus(target, options); // ideally, this would scroll to show the focus target

    onKeyDown = (e: any) => {
        e.stopPropagation();
        if (e.key === 'Tab' || e.key === 'Enter') {
            e.preventDefault();
        }
    };

    onPointerLeave = () => {
        const ele = document.getElementById('DashDocCommentView-' + this._props.docId) as HTMLDivElement;
        ele && (ele.style.backgroundColor = '');
    };

    onPointerEnter = () => {
        const ele = document.getElementById('DashDocCommentView-' + this._props.docId) as HTMLDivElement;
        ele && (ele.style.backgroundColor = 'orange');
    };

    componentWillUnmount = () => Object.values(this._disposers).forEach(disposer => disposer?.());
    isContentActive = () => this._props.tbox._props.isContentActive() || this._props.tbox.isAnyChildContentActive?.();

    render() {
        return !this._dashDoc || this._props.hidden ? null : (
            <div
                ref={this._spanRef}
                className="dash-span"
                style={{
                    width: `calc(100% - ${horizPadding}px)`,
                    height: this._height,
                    position: 'relative',
                    display: 'flex',
                    margin: 'auto',
                    pointerEvents: this.isContentActive() ? undefined : 'none',
                }}
                onPointerLeave={this.onPointerLeave}
                onPointerEnter={this.onPointerEnter}
                onKeyDown={this.onKeyDown}
                onKeyPress={e => e.stopPropagation()}
                onKeyUp={e => e.stopPropagation()}
                onWheel={e => e.preventDefault()}>
                <DocumentView
                    Document={this._dashDoc}
                    addDocument={returnFalse}
                    removeDocument={this.removeDoc}
                    isDocumentActive={returnFalse}
                    isContentActive={this.isContentActive}
                    styleProvider={this._textBox._props.styleProvider}
                    containerViewPath={this._textBox.DocumentView?.().docViewPath}
                    ScreenToLocalTransform={this.getDocTransform}
                    addDocTab={this._textBox._props.addDocTab}
                    pinToPres={returnFalse}
                    renderDepth={this._textBox._props.renderDepth + 1}
                    PanelWidth={this._dashDoc[Width]}
                    PanelHeight={this._dashDoc[Height]}
                    focus={this.outerFocus}
                    whenChildContentsActiveChanged={this._props.tbox.whenChildContentsActiveChanged}
                    dontRegisterView={false}
                    childFilters={this._props.tbox?._props.childFilters}
                    childFiltersByRanges={this._props.tbox?._props.childFiltersByRanges}
                    searchFilterDocs={this._props.tbox?._props.searchFilterDocs}
                />
            </div>
        );
    }
}

export class DashDocView {
    dom: HTMLSpanElement; // container for label and value
    root: any;

    constructor(node: any, view: any, getPos: any, tbox: FormattedTextBox) {
        this.dom = document.createElement('span');
        this.dom.style.position = 'relative';
        this.dom.style.textIndent = '0';
        this.dom.style.width = (+node.attrs.width.toString().replace('px', '') + horizPadding).toString();
        this.dom.style.height = node.attrs.height;
        this.dom.style.display = node.attrs.hidden ? 'none' : 'inline-block';
        (this.dom.style as any).float = node.attrs.float;
        this.dom.onkeypress = function (e: any) {
            e.stopPropagation();
        };
        this.dom.onkeydown = function (e: any) {
            e.stopPropagation();
        };
        this.dom.onkeyup = function (e: any) {
            e.stopPropagation();
        };
        this.dom.onmousedown = function (e: any) {
            e.stopPropagation();
        };

        this.root = ReactDOM.createRoot(this.dom);
        this.root.render(
            <DashDocViewInternal
                docId={node.attrs.docId}
                embedding={node.attrs.embedding}
                width={node.attrs.width}
                height={node.attrs.height}
                hidden={node.attrs.hidden}
                fieldKey={node.attrs.fieldKey}
                tbox={tbox}
                view={view}
                node={node}
                getPos={getPos}
            />
        );
    }
    destroy() {
        setTimeout(() => {
            try {
                this.root.unmount();
            } catch {
                /* empty */
            }
        });
    }
    deselectNode() {
        this.dom.style.backgroundColor = '';
    }
    selectNode() {
        this.dom.style.backgroundColor = 'rgb(141, 182, 247)';
    }
}
