/* eslint-disable jsx-a11y/label-has-associated-control */
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 { ClientUtils } from '../../../../ClientUtils';
import { Doc } from '../../../../fields/Doc';
import { NumCast, StrCast } from '../../../../fields/Types';
import { Networking } from '../../../Network';
import { GPTCallType, gptAPICall, gptImageCall } from '../../../apis/gpt/GPT';
import { Docs } from '../../../documents/Documents';
import { DocUtils } from '../../../documents/DocUtils';
import { ObservableReactComponent } from '../../ObservableReactComponent';
import { AnchorMenu } from '../AnchorMenu';
import './GPTPopup.scss';

export enum GPTPopupMode {
    SUMMARY,
    EDIT,
    IMAGE,
    DATA,
}

interface GPTPopupProps {}

@observer
export class GPTPopup extends ObservableReactComponent<GPTPopupProps> {
    // eslint-disable-next-line no-use-before-define
    static Instance: GPTPopup;
    @observable private chatMode: boolean = false;
    private correlatedColumns: string[] = [];

    @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 selectedText: string = '';
    @action
    public setSelectedText = (text: string) => {
        this.selectedText = text;
    };
    @observable
    public dataJson: string = '';
    public dataChatPrompt: string | null = null;
    @action
    public setDataJson = (text: string) => {
        if (text === '') this.dataChatPrompt = '';
        this.dataJson = 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 = () => {};

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

    // 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 createFilteredDoc: (axes?: any) => 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 undefined;
        this.setImgUrls([]);
        this.setMode(GPTPopupMode.IMAGE);
        this.setVisible(true);
        this.setLoading(true);

        try {
            const imageUrls = await gptImageCall(this.imgDesc);
            console.log('Image urls: ', imageUrls);
            if (imageUrls && imageUrls[0]) {
                const [result] = await Networking.PostToServer('/uploadRemoteImage', { sources: [imageUrls[0]] });
                console.log('Upload result: ', result);
                const source = ClientUtils.prepend(result.accessPaths.agnostic.client);
                console.log('Upload source: ', source);
                this.setImgUrls([[imageUrls[0], source]]);
            }
        } catch (err) {
            console.error(err);
        }
        this.setLoading(false);
        return undefined;
    };

    /**
     * Completes an API call to generate a summary of
     * this.selectedText in the popup.
     */
    generateSummary = async () => {
        GPTPopup.Instance.setVisible(true);
        GPTPopup.Instance.setMode(GPTPopupMode.SUMMARY);
        GPTPopup.Instance.setLoading(true);

        try {
            const res = await gptAPICall(this.selectedText, GPTCallType.SUMMARY);
            GPTPopup.Instance.setText(res || 'Something went wrong.');
        } catch (err) {
            console.error(err);
        }
        GPTPopup.Instance.setLoading(false);
    };

    /**
     * Completes an API call to generate an analysis of
     * this.dataJson in the popup.
     */
    generateDataAnalysis = async () => {
        GPTPopup.Instance.setVisible(true);
        GPTPopup.Instance.setLoading(true);
        try {
            const res = await gptAPICall(this.dataJson, GPTCallType.DATA, this.dataChatPrompt);
            const json = JSON.parse(res! as string);
            const keys = Object.keys(json);
            this.correlatedColumns = [];
            this.correlatedColumns.push(json[keys[0]]);
            this.correlatedColumns.push(json[keys[1]]);
            GPTPopup.Instance.setText(json[keys[2]] || 'Something went wrong.');
        } catch (err) {
            console.error(err);
        }
        GPTPopup.Instance.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);
        const anchor = AnchorMenu.Instance?.GetAnchor(undefined, false);
        if (anchor) {
            DocUtils.MakeLink(newDoc, anchor, {
                link_relationship: 'GPT Summary',
            });
        }
    };

    /**
     * Creates a histogram to show the correlation relationship that was found
     */
    private createVisualization = () => {
        this.createFilteredDoc(this.correlatedColumns);
    };

    /**
     * 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' });
    };

    /**
     * Creates a chatbox for analyzing data so that users can ask specific questions.
     */
    private chatWithAI = () => {
        this.chatMode = true;
    };
    dataPromptChanged = action((e: React.ChangeEvent<HTMLInputElement>) => {
        this.dataChatPrompt = e.target.value;
    });

    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 = () => (
        <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);
                                    },
                                ]}
                            />
                        ) : (
                            this.text
                        ))}
                </div>
            </div>
            {!this.loading && (
                <div className="btns-wrapper">
                    {this.done ? (
                        <>
                            <IconButton tooltip="Generate Again" onClick={this.generateSummary} 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>
            )}
        </>
    );

    dataAnalysisBox = () => (
        <>
            <div>
                {this.heading('ANALYSIS')}
                <div className="content-wrapper">
                    {!this.loading &&
                        (!this.done ? (
                            <TypeAnimation
                                speed={50}
                                sequence={[
                                    this.text,
                                    () => {
                                        setTimeout(() => {
                                            this.setDone(true);
                                        }, 500);
                                    },
                                ]}
                            />
                        ) : (
                            this.text
                        ))}
                </div>
            </div>
            {!this.loading && (
                <div className="btns-wrapper">
                    {this.done ? (
                        this.chatMode ? (
                            <input
                                defaultValue=""
                                autoComplete="off"
                                onChange={this.dataPromptChanged}
                                onKeyDown={e => {
                                    e.key === 'Enter' ? this.generateDataAnalysis() : null;
                                    e.stopPropagation();
                                }}
                                type="text"
                                placeholder="Ask GPT a question about the data..."
                                id="search-input"
                                className="searchBox-input"
                                style={{ width: '100%' }}
                            />
                        ) : (
                            <>
                                <Button tooltip="Transfer to text" text="Transfer To Text" onClick={this.transferToText} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
                                <Button tooltip="Create a graph to visualize the correlation results" text="Visualize" onClick={this.createVisualization} color={StrCast(Doc.UserDoc().userVariantColor)} type={Type.TERT} />
                                <Button tooltip="Chat with AI" text="Chat with AI" onClick={this.chatWithAI} 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>
        ) : null;

    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.DATA ? this.dataAnalysisBox() : this.mode === GPTPopupMode.IMAGE ? this.imageBox() : null}
            </div>
        );
    }
}
