import { IconProp } from '@fortawesome/fontawesome-svg-core';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
import { Tooltip } from '@mui/material';
import { MultiToggle, Type } from '@dash/components';
import { computed, makeObservable } from 'mobx';
import { observer } from 'mobx-react';
import * as React from 'react';
import { returnFalse, returnZero, setupMoveUpEvents } from '../../../ClientUtils';
import { emptyFunction } from '../../../Utils';
import { Doc, DocListCast } from '../../../fields/Doc';
import { BoolCast, NumCast, StrCast } from '../../../fields/Types';
import { SnappingManager } from '../../util/SnappingManager';
import { Transform } from '../../util/Transform';
import { ObservableReactComponent } from '../ObservableReactComponent';
import { DocumentView, DocumentViewProps } from '../nodes/DocumentView';
import './FlashcardPracticeUI.scss';

export enum practiceMode {
    PRACTICE = 'practice',
    QUIZ = 'quiz',
}
enum practiceVal {
    MISSED = 'missed',
    CORRECT = 'correct',
}

export enum flashcardRevealOp {
    FLIP = 'flip',
    SLIDE = 'slide',
}

interface PracticeUIProps {
    fieldKey: string;
    layoutDoc: Doc;
    filteredChildDocs: () => Doc[];
    allChildDocs: () => Doc[];
    curDoc: () => Doc | undefined;
    advance?: (correct: boolean) => void;
    renderDepth: number;
    sideBtnWidth: number;
    uiBtnScaling: number;
    ScreenToLocalBoxXf: () => Transform;
    docViewProps: () => DocumentViewProps;
    setFilterFunc: (func?: (doc: Doc) => boolean) => void;
}
@observer
export class FlashcardPracticeUI extends ObservableReactComponent<PracticeUIProps> {
    constructor(props: PracticeUIProps) {
        super(props);
        makeObservable(this);
        this._props.setFilterFunc(this.tryFilterOut);
    }

    componentWillUnmount(): void {
        this._props.setFilterFunc(undefined);
    }

    get practiceField() { return this._props.fieldKey + "_practice"; } // prettier-ignore

    @computed get filterDoc() { return DocListCast(Doc.MyContextMenuBtns.data).find(doc => doc.title === 'Filter'); } // prettier-ignore
    @computed get practiceMode() {  return this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? StrCast(this._props.layoutDoc.practiceMode) : ''; } // prettier-ignore

    btnHeight = () => NumCast(this.filterDoc?.height) * Math.min(1, this._props.ScreenToLocalBoxXf().Scale);
    btnWidth = () => (!this.filterDoc ? 1 : (this.btnHeight() * NumCast(this.filterDoc._width)) / NumCast(this.filterDoc._height));

    /**
     * Sets the practice mode answer style for flashcards
     * @param mode practiceMode or undefined for no practice
     */
    setPracticeMode = (mode: practiceMode | undefined) => {
        this._props.layoutDoc.practiceMode = mode;
        this._props.allChildDocs().map(doc => (doc[this.practiceField] = undefined));
    };

    @computed get emptyMessage() {
        const cardCount = this._props.filteredChildDocs().length;
        const practiceMessage = this.practiceMode && !Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount ? 'Finished! Click here to view all flashcards.' : '';
        const filterMessage = practiceMessage
            ? ''
            : Doc.hasDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny) && !cardCount
              ? 'No tagged items. Click here to view all flash cards.'
              : this.practiceMode && !cardCount
                ? 'No flashcards to show! Click here to leave practice mode'
                : '';
        return !practiceMessage && !filterMessage ? null : (
            <p
                className="FlashcardPracticeUI-message"
                style={{ transform: `scale(${this._props.uiBtnScaling})` }}
                onClick={() => {
                    if (filterMessage || practiceMessage) {
                        this.setPracticeMode(undefined);
                        Doc.setDocFilter(this._props.layoutDoc, 'tags', Doc.FilterAny, 'remove');
                    }
                }}>
                {filterMessage || practiceMessage}
            </p>
        );
    }

    @computed get practiceButtons() {
        /*
         * Sets a flashcard to either missed or correct depending on if they got the question right in practice mode.
         */
        const setPracticeVal = (e: React.MouseEvent, val: string) => {
            e.stopPropagation();
            const curDoc = this._props.curDoc();
            this._props.advance?.(val === practiceVal.CORRECT);
            curDoc && (curDoc[this.practiceField] = val);
        };

        return this.practiceMode == practiceMode.PRACTICE && this._props.curDoc() ? (
            <div className="FlashcardPracticeUI-practice" style={{ transform: `scale(${this._props.uiBtnScaling})`, bottom: `${this._props.sideBtnWidth}px`, height: `${this._props.sideBtnWidth}px` }}>
                <Tooltip title="Incorrect. View again later.">
                    <div key="remove" className="FlashcardPracticeUI-remove" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => setPracticeVal(e, practiceVal.MISSED))}>
                        <FontAwesomeIcon icon="xmark" color="red" size="1x" />
                    </div>
                </Tooltip>
                <Tooltip title="Correct">
                    <div key="check" className="FlashcardPracticeUI-check" onPointerDown={e => setupMoveUpEvents(this, e, returnFalse, emptyFunction, () => setPracticeVal(e, practiceVal.CORRECT))}>
                        <FontAwesomeIcon icon="check" color="green" size="1x" />
                    </div>
                </Tooltip>
            </div>
        ) : null;
    }
    @computed get practiceModesMenu() {
        const setColor = (mode: practiceMode) => (StrCast(this.practiceMode) === mode ? 'white' : 'lightgray');
        const togglePracticeMode = (mode: practiceMode) => this.setPracticeMode(mode === this.practiceMode ? undefined : mode);

        return !this._props.allChildDocs().some(doc => doc._layout_flashcardType) ? null : (
            <div
                className="FlashcardPracticeUI-practiceModes"
                style={{
                    transform: this._props.ScreenToLocalBoxXf().Scale >= 1 ? undefined : `translateY(${this.btnHeight() / this._props.ScreenToLocalBoxXf().Scale - this.btnHeight()}px)`,
                }}>
                <MultiToggle
                    tooltip="Practice flashcards one at a time"
                    type={Type.PRIM}
                    color={SnappingManager.userColor}
                    background={SnappingManager.userVariantColor}
                    multiSelect={false}
                    isToggle={false}
                    toggleStatus={!!this.practiceMode}
                    label="Practice"
                    items={[
                        [practiceMode.QUIZ, 'file-pen', 'Practice flashcards using GPT'],
                        [practiceMode.PRACTICE, 'check', this.practiceMode === practiceMode.PRACTICE ? 'Exit practice mode' : 'Practice flashcards manually'],
                    ].map(([item, icon, tooltip]) => ({
                        icon: <FontAwesomeIcon className={`FlashcardPracticeUI-${item}`} color={setColor(item as practiceMode)} icon={icon as IconProp} size="sm" />,
                        tooltip: tooltip,
                        val: item,
                    }))}
                    selectedItems={this.practiceMode}
                    onSelectionChange={(val: (string | number) | (string | number)[]) => togglePracticeMode(val as practiceMode)}
                />
                <MultiToggle
                    tooltip="How to reveal flashcard answer"
                    type={Type.PRIM}
                    color={SnappingManager.userColor}
                    background={SnappingManager.userVariantColor}
                    multiSelect={false}
                    isToggle={false}
                    toggleStatus={!!this.practiceMode}
                    label={StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)}
                    items={[
                        ['reveal', StrCast(this._props.layoutDoc.revealOp) === flashcardRevealOp.SLIDE ? 'expand' : 'question', StrCast(this._props.layoutDoc.revealOp, flashcardRevealOp.FLIP)],
                        ['trigger', this._props.layoutDoc.revealOp_hover ? 'hand-point-up' : 'hand', this._props.layoutDoc.revealOp_hover ? 'show on hover' : 'show on click'],
                    ].map(([item, icon, tooltip]) => ({
                        icon: <FontAwesomeIcon className={`FlashcardPracticeUI-${item}`} color={setColor(item as practiceMode)} icon={icon as IconProp} size="sm" />,
                        tooltip: tooltip,
                        val: item,
                    }))}
                    selectedItems={this._props.layoutDoc.revealOp_hover ? ['reveal', 'trigger'] : 'reveal'}
                    onSelectionChange={(val: (string | number) | (string | number)[]) => {
                        if (val === 'reveal') this._props.layoutDoc.revealOp = this._props.layoutDoc.revealOp === flashcardRevealOp.SLIDE ? flashcardRevealOp.FLIP : flashcardRevealOp.SLIDE;
                        if (val === 'trigger') this._props.layoutDoc.revealOp_hover = !this._props.layoutDoc.revealOp_hover;
                    }}
                />
            </div>
        );
    }
    tryFilterOut = (doc: Doc) => (this.practiceMode && doc?._layout_flashcardType && doc[this.practiceField] === practiceVal.CORRECT ? true : false); // show only cards that aren't marked as correct
    render() {
        return (
            <div className="FlashcardPracticeUI">
                {this.emptyMessage}
                {this.practiceButtons}
                {this._props.layoutDoc._chromeHidden ? null : (
                    <div className="FlashcardPracticeUI-menu" style={{ height: this.btnHeight(), width: this.btnHeight(), transform: `scale(${this._props.uiBtnScaling})` }}>
                        {!this.filterDoc ? null : (
                            <DocumentView
                                {...this._props.docViewProps()}
                                Document={this.filterDoc}
                                TemplateDataDocument={undefined}
                                PanelWidth={this.btnWidth}
                                PanelHeight={this.btnHeight}
                                NativeWidth={returnZero}
                                NativeHeight={returnZero}
                                hideDecorations={BoolCast(this._props.layoutDoc.layout_hideDecorations)}
                                hideCaptions={true}
                                hideFilterStatus={true}
                                renderDepth={this._props.renderDepth + 1}
                                fitWidth={undefined}
                                showTags={false}
                                setContentViewBox={undefined}
                            />
                        )}
                        {this.practiceModesMenu}
                    </div>
                )}
            </div>
        );
    }
}
