import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Button, IconButton, Type } from 'browndash-components';
import { action, makeObservable, observable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { CgClose } from 'react-icons/cg';
import ReactLoading from 'react-loading';
import { TypeAnimation } from 'react-type-animation';
import { Utils } from '../../../../Utils';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
import { Networking } from '../../../Network';
import { gptImageCall } from '../../../apis/gpt/GPT';
import { DocUtils, Docs } from '../../../documents/Documents';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { AnchorMenu } from '../AnchorMenu';
import './GPTPopup.scss';

export enum GPTPopupMode {
    SUMMARY,
    EDIT,
    IMAGE,
}

interface GPTPopupProps {}

@observer
export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
    static Instance: GPTPopup;

    @observable
    public visible: boolean = false;
    @action
    public setVisible = (vis: boolean) => {
        this.visible = vis;
    };
    @observable
    public loading: boolean = false;
    @action
    public setLoading = (loading: boolean) => {
        this.loading = loading;
    };
    @observable
    public text: string = '';
    @action
    public setText = (text: string) => {
        this.text = text;
    };

    @observable
    public imgDesc: string = '';
    @action
    public setImgDesc = (text: string) => {
        this.imgDesc = text;
    };

    @observable
    public imgUrls: string[][] = [];
    @action
    public setImgUrls = (imgs: string[][]) => {
        this.imgUrls = imgs;
    };

    @observable
    public mode: GPTPopupMode = GPTPopupMode.SUMMARY;
    @action
    public setMode = (mode: GPTPopupMode) => {
        this.mode = mode;
    };

    @observable
    public highlightRange: number[] = [];
    @action callSummaryApi = () => {};
    @action callEditApi = () => {};
    @action replaceText = (replacement: string) => {};

    @observable
    private done: boolean = false;
    @action
    public setDone = (done: boolean) => {
        this.done = done;
    };

    // change what can be a ref into a ref
    @observable
    private sidebarId: string = '';
    @action
    public setSidebarId = (id: string) => {
        this.sidebarId = id;
    };

    @observable
    private imgTargetDoc: Doc | undefined;
    @action
    public setImgTargetDoc = (anchor: Doc) => {
        this.imgTargetDoc = anchor;
    };

    @observable
    private textAnchor: Doc | undefined;
    @action
    public setTextAnchor = (anchor: Doc) => {
        this.textAnchor = anchor;
    };

    public addDoc: (doc: Doc | Doc[], sidebarKey?: string | undefined) => boolean = () => false;
    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;

    /**
     * Generates a Dalle image and uploads it to the server.
     */
    generateImage = async () => {
        if (this.imgDesc === '') return;
        this.setImgUrls([]);
        this.setMode(GPTPopupMode.IMAGE);
        this.setVisible(true);
        this.setLoading(true);

        try {
            let image_urls = await gptImageCall(this.imgDesc);
            if (image_urls && image_urls[0]) {
                // need to fix this
                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [image_urls[0]] });
                console.log('Result', result);
                console.log('Client', result.accessPaths.agnostic.client);
                const source = Utils.prepend(result.accessPaths.agnostic.client);
                this.setImgUrls([[image_urls[0], source]]);
            }
        } catch (err) {
            console.log(err);
        }
        this.setLoading(false);
    };

    /**
     * Transfers the summarization text to a sidebar annotation text document.
     */
    private transferToText = () => {
        const newDoc = Docs.Create.TextDocument(this.text.trim(), {
            _width: 200,
            _height: 50,
            _layout_fitWidth: true,
            _layout_autoHeight: true,
        });
        this.addDoc(newDoc, this.sidebarId);
        // newDoc.data = 'Hello world';
        const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
        if (anchor) {
            DocUtils.MakeLink(newDoc, anchor, {
                link_relationship: 'GPT Summary',
            });
        }
    };

    /**
     * Transfers the image urls to actual image docs
     */
    private transferToImage = (source: string) => {
        const textAnchor = this.imgTargetDoc;
        if (!textAnchor) return;
        const newDoc = Docs.Create.ImageDocument(source, {
            x: NumCast(textAnchor.x) + NumCast(textAnchor._width) + 10,
            y: NumCast(textAnchor.y),
            _height: 200,
            _width: 200,
            data_nativeWidth: 1024,
            data_nativeHeight: 1024,
        });
        if (Doc.IsInMyOverlay(textAnchor)) {
            newDoc.overlayX = textAnchor.x;
            newDoc.overlayY = NumCast(textAnchor.y) + NumCast(textAnchor._height);
            Doc.AddToMyOverlay(newDoc);
        } else {
            this.addToCollection?.(newDoc);
        }
        // Create link between prompt and image
        DocUtils.MakeLink(textAnchor, newDoc, { link_relationship: 'Image Prompt' });
    };

    private getPreviewUrl = (source: string) => source.split('.').join('_m.');

    constructor(props: GPTPopupProps) {
        super(props);
        makeObservable(this);
        GPTPopup.Instance = this;
    }

    componentDidUpdate = () => {
        if (this.loading) {
            this.setDone(false);
        }
    };

    imageBox = () => {
        return (
            <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                {this.heading('GENERATED IMAGE')}
                <div className="image-content-wrapper">
                    {this.imgUrls.map(rawSrc => (
                        <div className="img-wrapper">
                            <div className="img-container">
                                <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
                            </div>
                            <div className="btn-container">
                                <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
                            </div>
                        </div>
                    ))}
                </div>
                {!this.loading && (
                    <>
                        <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
                    </>
                )}
            </div>
        );
    };

    data = () => {
        return (
            <div style={{ display: 'flex', flexDirection: 'column', gap: '1rem' }}>
                {this.heading('GENERATED IMAGE')}
                <div className="image-content-wrapper">
                    {this.imgUrls.map(rawSrc => (
                        <div className="img-wrapper">
                            <div className="img-container">
                                <img key={rawSrc[0]} src={rawSrc[0]} width={150} height={150} alt="dalle generation" />
                            </div>
                            <div className="btn-container">
                                <Button text="Save Image" onClick={() => this.transferToImage(rawSrc[1])} color={StrCast(Doc.UserDoc().userColor)} type={Type.TERT} />
                            </div>
                        </div>
                    ))}
                </div>
                {!this.loading && (
                    <>
                        <IconButton tooltip="Generate Again" onClick={this.generateImage} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
                    </>
                )}
            </div>
        );
    };

    summaryBox = () => (
        <>
            <div>
                {this.heading('SUMMARY')}
                <div className="content-wrapper">
                    {!this.loading &&
                        (!this.done ? (
                            <TypeAnimation
                                speed={50}
                                sequence={[
                                    this.text,
                                    () => {
                                        setTimeout(() => {
                                            this.setDone(true);
                                        }, 500);
                                    },
                                ]}
                                //cursor={{ hideWhenDone: true }}
                            />
                        ) : (
                            this.text
                        ))}
                </div>
            </div>
            {!this.loading && (
                <div className="btns-wrapper">
                    {this.done ? (
                        <>
                            <IconButton tooltip="Generate Again" onClick={this.callSummaryApi} icon={<FontAwesomeIcon icon="redo-alt" size="lg" />} color={StrCast(Doc.UserDoc().userVariantColor)} />
                            <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
                        </>
                    ) : (
                        <div className="summarizing">
                            <span>Summarizing</span>
                            <ReactLoading type="bubbles" color="#bcbcbc" width={20} height={20} />
                            <Button
                                text="Stop Animation"
                                onClick={() => {
                                    this.setDone(true);
                                }}
                                color={StrCast(Doc.UserDoc().userVariantColor)}
                                type={Type.TERT}
                            />
                        </div>
                    )}
                </div>
            )}
        </>
    );

    aiWarning = () =>
        this.done ? (
            <div className="ai-warning">
                <FontAwesomeIcon icon="exclamation-circle" size="sm" style={{ paddingRight: '5px' }} />
                AI generated responses can contain inaccurate or misleading content.
            </div>
        ) : (
            <></>
        );

    heading = (headingText: string) => (
        <div className="summary-heading">
            <label className="summary-text">{headingText}</label>
            {this.loading ? <ReactLoading type="spin" color="#bcbcbc" width={14} height={14} /> : <IconButton color={StrCast(Doc.UserDoc().userVariantColor)} tooltip="close" icon={<CgClose size="16px" />} onClick={() => this.setVisible(false)} />}
        </div>
    );

    render() {
        return (
            <div className="summary-box" style={{ display: this.visible ? 'flex' : 'none' }}>
                {this.mode === GPTPopupMode.SUMMARY ? this.summaryBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : <></>}
            </div>
        );
    }
}
