import { makeObservable, action, observable, autorun } from 'mobx';
import * as React from 'react';
import { Docs } from '../../../documents/Documents';
import { DocumentType } from '../../../documents/DocumentTypes';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import { FormattedTextBox, FormattedTextBoxProps } from './FormattedTextBox';
import { gptAPICall, GPTCallType, gptImageLabel } from '../../../apis/gpt/GPT';
import { RichTextField } from '../../../../fields/RichTextField';
import { TextSelection } from 'prosemirror-state';
import { Plugin } from 'prosemirror-state';

export class DailyJournal extends ViewBoxAnnotatableComponent<FieldViewProps>() {
    @observable journalDate: string;
    @observable typingTimeout: NodeJS.Timeout | null = null; // Track typing delay
    @observable lastUserText: string = ''; // Store last user-entered text
    _ref = React.createRef<FormattedTextBox>(); // reference to the formatted textbox
    predictiveTextRange: { from: number; to: number } | null = null; // where predictive text starts and ends
    private predictiveText: string | null = ' ... why?';

    public static LayoutString(fieldStr: string) {
        return FieldView.LayoutString(DailyJournal, fieldStr);
    }

    constructor(props: FormattedTextBoxProps) {
        super(props);
        makeObservable(this);
        this.journalDate = this.getFormattedDate();
    }

    /**
     * Method to get the current date in standard format
     * @returns - date in standard long format
     */

    getFormattedDate(): string {
        const date = new Date().toLocaleDateString(undefined, {
            weekday: 'long',
            year: 'numeric',
            month: 'long',
            day: 'numeric',
        });
        console.log('getFormattedDate():', date);
        return date;
    }

    /**
     * Method to set the title of the node to the date
     */
    @action
    setDailyTitle() {
        console.log('setDailyTitle() called...');
        console.log('Current title before update:', this.dataDoc.title);

        if (!this.dataDoc.title || this.dataDoc.title !== this.journalDate) {
            console.log('Updating title to:', this.journalDate);
            this.dataDoc.title = this.journalDate;
        }

        console.log('New title after update:', this.dataDoc.title);
    }

    /**
     * Method to set the standard text of the node (to the current date)
     */
    @action
    setDailyText() {
        const placeholderText = 'Start writing here...';
        const dateText = `${this.journalDate}\n`;

        console.log('Checking if dataDoc has text field...');

        this.dataDoc[this.fieldKey] = RichTextField.textToRtfFormat(
            [
                { text: 'Journal Entry:', styles: { bold: true, color: 'black', fontSize: 20 } },
                { text: dateText, styles: { italic: true, color: 'gray', fontSize: 15 } },
                { text: placeholderText, styles: { fontSize: 14, color: 'gray' } },
            ],
            undefined,
            placeholderText.length
        );

        console.log('Current text field:', this.dataDoc[this.fieldKey]);
    }

    /**
     * Tracks user typing text inout into the node, to call the insert predicted
     * text function when appropriate (i.e. when the user stops typing)
     */

    @action onTextInput = () => {
        const editorView = this._ref.current?.EditorView;
        if (!editorView) return;

        if (this.typingTimeout) clearTimeout(this.typingTimeout);

        this.typingTimeout = setTimeout(() => {
            this.insertPredictiveQuestion();
        }, 3500);
    };

    /**
     * Inserts predictive text at the end of what the user is typing
     */

    @action insertPredictiveQuestion = () => {
        const editorView = this._ref.current?.EditorView;
        if (!editorView) return;

        const { state, dispatch } = editorView;
        const { schema } = state;
        const { from, to } = state.selection;
        const insertPos = to; // cursor position

        const resolvedPos = state.doc.resolve(insertPos);
        const parentNode = resolvedPos.parent;
        const indexInParent = resolvedPos.index();
        const isAtEndOfParent = indexInParent >= parentNode.childCount;

        // Check if there's a line break or paragraph node after the current position
        let hasNewlineAfter = false;
        try {
            const nextNode = parentNode.child(indexInParent);
            hasNewlineAfter = nextNode.type.name === 'hard_break' || nextNode.type.name === 'paragraph';
        } catch {
            hasNewlineAfter = false;
        }

        // Only insert if we're at end of node, or there's a newline node after
        if (!isAtEndOfParent && !hasNewlineAfter) return;

        const fontSizeMark = schema.marks.pFontSize.create({ fontSize: '14px' });
        const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'lightgray' });
        const fontItalicsMark = schema.marks.em.create();

        // styled text node
        const text = ' ... why?';
        const predictedText = schema.text(text, [fontSizeMark, fontColorMark, fontItalicsMark]);

        // Insert styled text at cursor position
        const transaction = state.tr.insert(insertPos, predictedText);
        dispatch(transaction);

        this.predictiveText = text;
    };

    createPredictiveCleanupPlugin = () => {
        return new Plugin({
            view: () => {
                return {
                    update: (view, prevState) => {
                        const { state, dispatch } = view;
                        if (!this.predictiveText) return;

                        // Check if doc or selection changed
                        if (!prevState.doc.eq(state.doc) || !prevState.selection.eq(state.selection)) {
                            let found = false;
                            const textToRemove = this.predictiveText;

                            state.doc.descendants((node, pos) => {
                                if (node.isText && node.text === textToRemove) {
                                    const tr = state.tr.delete(pos, pos + node.nodeSize);

                                    // Set the desired default marks for future input
                                    const fontSizeMark = state.schema.marks.pFontSize.create({ fontSize: '14px' });
                                    const fontColorMark = state.schema.marks.pFontColor.create({ fontColor: 'gray' });
                                    tr.setStoredMarks([]);
                                    tr.setStoredMarks([fontSizeMark, fontColorMark]);

                                    dispatch(tr);

                                    this.predictiveText = null;
                                    return false;
                                }
                                return true;
                            });

                            if (!found) {
                                // fallback cleanup
                                this.predictiveText = null;
                            }
                        }
                    },
                };
            },
        });
    };

    componentDidMount(): void {
        console.log('componentDidMount() triggered...');
        console.log('Text: ' + (this.Document.text as any)?.Text);

        const editorView = this._ref.current?.EditorView;
        if (editorView) {
            editorView.dom.addEventListener('input', this.onTextInput);

            // Add plugin to state if not already added
            const cleanupPlugin = this.createPredictiveCleanupPlugin();
            const newState = editorView.state.reconfigure({
                plugins: [...editorView.state.plugins, cleanupPlugin],
            });
            editorView.updateState(newState);
        }

        const rawText = (this.Document.text as any)?.Text ?? '';
        const isTextEmpty = !rawText || rawText === '';

        const currentTitle = this.dataDoc.title || '';
        const isTitleString = typeof currentTitle === 'string';
        const isDefaultTitle = isTitleString && currentTitle.includes('Untitled DailyJournal');

        if (isTextEmpty && isDefaultTitle) {
            console.log('Journal title and text are default. Initializing...');
            this.setDailyTitle();
            this.setDailyText();
        } else {
            console.log('Journal already has content. Skipping initialization.');
        }
    }

    componentWillUnmount(): void {
        const editorView = this._ref.current?.EditorView;
        if (editorView) {
            editorView.dom.removeEventListener('input', this.onTextInput);
        }
        if (this.typingTimeout) clearTimeout(this.typingTimeout);
    }

    @action handleGeneratePrompts = async () => {
        const rawText = (this.Document.text as any)?.Text ?? '';
        console.log('Extracted Journal Text:', rawText);
        console.log('Before Update:', this.Document.text, 'Type:', typeof this.Document.text);

        if (!rawText.trim()) {
            alert('Journal is empty! Write something first.');
            return;
        }

        try {
            // Call GPT API to generate prompts
            const res = await gptAPICall('Generate 1-2 short journal prompts for the following journal entry: ' + rawText, GPTCallType.COMPLETION);

            if (!res) {
                console.error('GPT call failed.');
                return;
            }

            const editorView = this._ref.current?.EditorView;
            if (!editorView) {
                console.error('EditorView is not available.');
                return;
            } else {
                const { state, dispatch } = editorView;
                const { schema } = state;

                // Use available marks
                const boldMark = schema.marks.strong.create();
                const italicMark = schema.marks.em.create();
                const fontSizeMark = schema.marks.pFontSize.create({ fontSize: '14px' });
                const fontColorMark = schema.marks.pFontColor.create({ fontColor: 'gray' });

                // Create text nodes with formatting
                const headerText = schema.text('\n\n# Suggested Prompts:\n', [boldMark, italicMark, fontSizeMark, fontColorMark]);
                const responseText = schema.text(res, [fontSizeMark, fontColorMark]);

                // Insert formatted text
                const transaction = state.tr.insert(state.selection.from, headerText).insert(state.selection.from + headerText.nodeSize, responseText);
                dispatch(transaction);
            }
        } catch (err) {
            console.error('Error calling GPT:', err);
        }
    };

    render() {
        return (
            <div
                style={{
                    // background: 'beige',
                    width: '100%',
                    height: '100%',
                    backgroundColor: 'beige',
                    backgroundImage: `
                    repeating-linear-gradient(
                        to bottom, 
                        rgba(255, 26, 26, 0.2) 0px, rgba(255, 26, 26, 0.2) 1px,  /* Thin red stripes */
                        transparent 1px, transparent 20px
                    )
                `,
                    backgroundSize: '100% 20px',
                    backgroundRepeat: 'repeat',
                }}>
                {/* GPT Button */}
                <button
                    style={{
                        position: 'absolute',
                        bottom: '5px',
                        right: '5px',
                        padding: '5px 10px',
                        backgroundColor: '#9EAD7C',
                        color: 'white',
                        border: 'none',
                        borderRadius: '5px',
                        cursor: 'pointer',
                        zIndex: 10,
                    }}
                    onClick={this.handleGeneratePrompts}>
                    Prompts
                </button>

                <FormattedTextBox ref={this._ref} {...this._props} fieldKey={'text'} Document={this.Document} TemplateDataDocument={undefined} />
            </div>
        );
    }
}

Docs.Prototypes.TemplateMap.set(DocumentType.JOURNAL, {
    layout: { view: DailyJournal, dataField: 'text' },
    options: {
        acl: '',
        _height: 35,
        _xMargin: 10,
        _yMargin: 10,
        _layout_autoHeight: true,
        _layout_nativeDimEditable: true,
        _layout_reflowVertical: true,
        _layout_reflowHorizontal: true,
        defaultDoubleClick: 'ignore',
        systemIcon: 'BsFileEarmarkTextFill',
    },
});
