import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { ColorPicker, Group, IconButton, Popup, Size, Toggle, ToggleType, Type } from 'browndash-components';
import { IReactionDisposer, ObservableMap, action, computed, makeObservable, observable, reaction } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { ColorResult } from 'react-color';
import { ClientUtils, returnFalse, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction, unimplementedFunction } from '../../../Utils';
import { Doc, Opt } from '../../../fields/Doc';
import { GPTCallType, gptAPICall } from '../../apis/gpt/GPT';
import { Docs } from '../../documents/Documents';
import { SettingsManager } from '../../util/SettingsManager';
import { AntimodeMenu, AntimodeMenuProps } from '../AntimodeMenu';
import { LinkPopup } from '../linking/LinkPopup';
import { DocumentView } from '../nodes/DocumentView';
import './AnchorMenu.scss';
import { GPTPopup, GPTPopupMode } from './GPTPopup/GPTPopup';

@observer
export class AnchorMenu extends AntimodeMenu<AntimodeMenuProps> {
    // eslint-disable-next-line no-use-before-define
    static Instance: AnchorMenu;

    private _disposer: IReactionDisposer | undefined;
    private _commentRef = React.createRef<HTMLDivElement>();
    private _cropRef = React.createRef<HTMLDivElement>();

    constructor(props: AntimodeMenuProps) {
        super(props);
        makeObservable(this);
        AnchorMenu.Instance = this;
        AnchorMenu.Instance._canFade = false;
    }

    @observable private highlightColor: string = 'rgba(245, 230, 95, 0.616)';

    @observable public Status: 'marquee' | 'annotation' | '' = '';

    // GPT additions
    @observable private _selectedText: string = '';
    @action
    public setSelectedText = (txt: string) => {
        this._selectedText = txt.trim();
    };

    public onMakeAnchor: () => Opt<Doc> = () => undefined; // Method to get anchor from text search

    public OnCrop: (e: PointerEvent) => void = unimplementedFunction;
    public OnClick: (e: PointerEvent) => void = unimplementedFunction;
    public OnAudio: (e: PointerEvent) => void = unimplementedFunction;
    public StartDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
    public StartCropDrag: (e: PointerEvent, ele: HTMLElement) => void = unimplementedFunction;
    public Highlight: (color: string) => void = emptyFunction;
    public GetAnchor: (savedAnnotations: Opt<ObservableMap<number, HTMLDivElement[]>>, addAsAnnotation: boolean) => Opt<Doc> = emptyFunction;
    public Delete: () => void = unimplementedFunction;
    public PinToPres: () => void = unimplementedFunction;
    public MakeTargetToggle: () => void = unimplementedFunction;
    public ShowTargetTrail: () => void = unimplementedFunction;
    public IsTargetToggler: () => boolean = returnFalse;
    public get Active() {
        return this._left > 0;
    }
    public addToCollection: ((doc: Doc | Doc[], annotationKey?: string | undefined) => boolean) | undefined;

    componentWillUnmount() {
        this._disposer?.();
    }

    componentDidMount() {
        this._disposer = reaction(
            () => DocumentView.Selected().slice(),
            () => AnchorMenu.Instance.fadeOut(true)
        );
    }

    /**
     * Invokes the API with the selected text and stores it in the summarized text.
     * @param e pointer down event
     */
    gptSummarize = 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);
    };
    // gptSummarize = async () => {
    //     GPTPopup.Instance?.setSelectedText(this._selectedText);
    //     GPTPopup.Instance.generateSummary();
    // };

    /**
     * Invokes the API with the selected text and stores it in the selected text.
     * @param e pointer down event
     */
    gptFlashcards = async () => {
        const queryText = this._selectedText;
        try {
            const res = await gptAPICall(queryText, GPTCallType.FLASHCARD);
            console.log(res);
            GPTPopup.Instance.setText(res || 'Something went wrong.');
            this.transferToFlashcard(res || 'Something went wrong');
        } catch (err) {
            console.error(err);
        }
        GPTPopup.Instance.setLoading(false);
    };

    /*
     * Transfers the flashcard text generated by GPT on flashcards and creates a collection out them.
     */
    transferToFlashcard = (text: string) => {
        // put each question generated by GPT on the front of the flashcard
        const senArr = text.split('Question');
        const collectionArr: Doc[] = [];
        for (let i = 1; i < senArr.length; i++) {
            console.log('Arr ' + i + ': ' + senArr[i]);
            const newDoc = Docs.Create.ComparisonDocument(senArr[i], { _layout_isFlashcard: true, _width: 300, _height: 300 });
            newDoc.text = senArr[i];
            collectionArr.push(newDoc);
        }
        // create a new carousel collection of these flashcards
        const newCol = Docs.Create.CarouselDocument(collectionArr, {
            _width: 250,
            _height: 200,
            _layout_fitWidth: false,
            _layout_autoHeight: true,
        });

        this.addToCollection?.(newCol); //this._props.addDocument(newCol)
    };

    pointerDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(
            this,
            e,
            (moveEv: PointerEvent) => {
                this.StartDrag(moveEv, this._commentRef.current!);
                return true;
            },
            returnFalse,
            clickEv => this.OnClick?.(clickEv)
        );
    };

    audioDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(this, e, returnFalse, returnFalse, clickEv => this.OnAudio?.(clickEv));
    };

    cropDown = (e: React.PointerEvent) => {
        setupMoveUpEvents(
            this,
            e,
            (moveEv: PointerEvent) => {
                this.StartCropDrag(moveEv, this._cropRef.current!);
                return true;
            },
            returnFalse,
            clickev => this.OnCrop?.(clickev)
        );
    };

    @action
    highlightClicked = () => {
        this.Highlight(this.highlightColor);
        AnchorMenu.Instance.fadeOut(true);
    };

    @computed get highlighter() {
        return (
            <Group>
                <IconButton
                    icon={<FontAwesomeIcon icon="highlighter" style={{ transition: 'transform 0.1s', transform: 'rotate(-45deg)' }} />}
                    tooltip="Click to Highlight"
                    onClick={this.highlightClicked}
                    colorPicker={this.highlightColor}
                    color={SettingsManager.userColor}
                />
                <ColorPicker selectedColor={this.highlightColor} setFinalColor={this.changeHighlightColor} setSelectedColor={this.changeHighlightColor} size={Size.XSMALL} />
            </Group>
        );
    }

    @action changeHighlightColor = (color: string) => {
        const col: ColorResult = {
            hex: color,
            hsl: { a: 0, h: 0, s: 0, l: 0 },
            rgb: { a: 0, r: 0, b: 0, g: 0 },
        };
        this.highlightColor = ClientUtils.colorString(col);
    };

    render() {
        const buttons =
            this.Status === 'marquee' ? (
                <>
                    {this.highlighter}
                    <div ref={this._commentRef}>
                        <IconButton
                            tooltip="Drag to Place Annotation" //
                            onPointerDown={this.pointerDown}
                            icon={<FontAwesomeIcon icon="comment-alt" />}
                            color={SettingsManager.userColor}
                        />
                    </div>
                    {/* GPT Summarize icon only shows up when text is highlighted, not on marquee selection */}
                    {this._selectedText && (
                        <IconButton
                            tooltip="Summarize with AI" //
                            onPointerDown={this.gptSummarize}
                            icon={<FontAwesomeIcon icon="comment-dots" size="lg" />}
                            color={SettingsManager.userColor}
                        />
                    )}
                    {/* Adds a create flashcards option to the anchor menu, which calls the gptFlashcard method. */}
                    <IconButton
                        tooltip="Create flashcards" //
                        onPointerDown={this.gptFlashcards}
                        icon={<FontAwesomeIcon icon="id-card" size="lg" />}
                        color={SettingsManager.userColor}
                    />
                    {AnchorMenu.Instance.OnAudio === unimplementedFunction ? null : (
                        <IconButton
                            tooltip="Click to Record Annotation" //
                            onPointerDown={this.audioDown}
                            icon={<FontAwesomeIcon icon="microphone" />}
                            color={SettingsManager.userColor}
                        />
                    )}
                    <Popup
                        tooltip="Find document to link to selected text" //
                        type={Type.PRIM}
                        icon={<FontAwesomeIcon icon="search" />}
                        popup={<LinkPopup key="popup" linkCreateAnchor={this.onMakeAnchor} />}
                        color={SettingsManager.userColor}
                    />
                    {AnchorMenu.Instance.StartCropDrag === unimplementedFunction ? null : (
                        <div ref={this._cropRef}>
                            <IconButton
                                tooltip="Click/Drag to create cropped image" //
                                onPointerDown={this.cropDown}
                                icon={<FontAwesomeIcon icon="image" />}
                                color={SettingsManager.userColor}
                            />
                        </div>
                    )}
                </>
            ) : (
                <>
                    {this.Delete !== returnFalse && (
                        <IconButton
                            tooltip="Remove Link Anchor" //
                            onPointerDown={this.Delete}
                            icon={<FontAwesomeIcon icon="trash-alt" />}
                            color={SettingsManager.userColor}
                        />
                    )}
                    {this.PinToPres !== returnFalse && (
                        <IconButton
                            tooltip="Pin to Presentation" //
                            onPointerDown={this.PinToPres}
                            icon={<FontAwesomeIcon icon="map-pin" />}
                            color={SettingsManager.userColor}
                        />
                    )}
                    {this.ShowTargetTrail !== returnFalse && (
                        <IconButton
                            tooltip="Show Linked Trail" //
                            onPointerDown={this.ShowTargetTrail}
                            icon={<FontAwesomeIcon icon="taxi" />}
                            color={SettingsManager.userColor}
                        />
                    )}
                    {this.IsTargetToggler !== returnFalse && (
                        <Toggle
                            tooltip="Make target visibility toggle on click"
                            type={Type.PRIM}
                            toggleType={ToggleType.BUTTON}
                            toggleStatus={this.IsTargetToggler()}
                            onClick={this.MakeTargetToggle}
                            icon={<FontAwesomeIcon icon="thumbtack" />}
                            color={SettingsManager.userColor}
                        />
                    )}
                </>
            );

        return this.getElement(buttons);
    }
}
