import { MathJaxContext } from 'better-react-mathjax';
import { action, makeObservable, observable, observe, reaction, runInAction } from 'mobx';
import { observer } from 'mobx-react';
import OpenAI, { ClientOptions } from 'openai';
import { ImageFile, Message } from 'openai/resources/beta/threads/messages';
import { RunStep } from 'openai/resources/beta/threads/runs/steps';
import * as React from 'react';
import { Doc } from '../../../../fields/Doc';
import { Id } from '../../../../fields/FieldSymbols';
import { CsvCast, DocCast, PDFCast, StrCast } from '../../../../fields/Types';
import { CsvField } from '../../../../fields/URLField';
import { Networking } from '../../../Network';
import { DocUtils } from '../../../documents/DocUtils';
import { DocumentType } from '../../../documents/DocumentTypes';
import { Docs } from '../../../documents/Documents';
import { DocumentManager } from '../../../util/DocumentManager';
import { LinkManager } from '../../../util/LinkManager';
import { ViewBoxAnnotatableComponent } from '../../DocComponent';
import { FieldView, FieldViewProps } from '../FieldView';
import './ChatBox.scss';
import MessageComponent from './MessageComponent';
import { ASSISTANT_ROLE, AssistantMessage, AI_Document, convertToAIDocument } from './types';
import { Annotation } from 'mobx/dist/internal';
import { FormEvent } from 'react';
import { url } from 'inspector';
import { Vectorstore } from './vectorstore/VectorstoreUpload';
import { DocumentView } from '../DocumentView';
import { CollectionFreeFormDocumentView } from '../CollectionFreeFormDocumentView';
import { CollectionFreeFormView } from '../../collections/collectionFreeForm';

@observer
export class ChatBox extends ViewBoxAnnotatableComponent<FieldViewProps>() {
    @observable history: AssistantMessage[] = [];
    @observable.deep current_message: AssistantMessage | undefined = undefined;

    @observable isLoading: boolean = false;
    @observable isInitializing: boolean = true;
    @observable expandedScratchpadIndex: number | null = null;
    @observable linked_docs_to_add: Doc[] = [];
    @observable inputValue: string = '';
    private openai: OpenAI;
    private documents: AI_Document[] = [];
    private _oldWheel: any;
    private vectorstore: Vectorstore;

    public static LayoutString(fieldKey: string) {
        return FieldView.LayoutString(ChatBox, fieldKey);
    }

    constructor(props: FieldViewProps) {
        super(props);
        makeObservable(this);
        this.openai = this.initializeOpenAI();
        this.history = [{ role: ASSISTANT_ROLE.ASSISTANT, text: 'Welcome to the Document Analyser Assistant! Link a document or ask questions to get started.' }];
        this.openai = this.initializeOpenAI();
        this.getLinkedDocs();
        this.vectorstore = new Vectorstore();

        reaction(
            () => this.history.map((msg: AssistantMessage) => ({ role: msg.role, text: msg.text, follow_up_questions: msg.follow_up_questions, citations: msg.citations })),
            serializableHistory => {
                this.dataDoc.data = JSON.stringify(serializableHistory);
            }
        );
    }

    getLinkedDocs = async () => {
        const visual_docs = (CollectionFreeFormDocumentView.from(this._props.DocumentView?.())?._props.parent as CollectionFreeFormView)?.childDocs.filter(doc => doc != this.Document);
        console.log('All Docs:', visual_docs);

        visual_docs?.forEach(async doc => {
            const local_file_path: string = CsvCast(doc.data, PDFCast(doc.data)).url?.pathname;

            if (local_file_path) {
                const { document_json } = await Networking.PostToServer('/createDocument', { file_path: local_file_path });
                const ai_document: AI_Document = convertToAIDocument(document_json);
                this.documents.push(ai_document);
                await this.vectorstore.addDocument(ai_document);
                doc['ai_document'] = document_json;
            }
        });
    };

    @action
    uploadNewDocument = async (newDoc: Doc) => {
        const local_file_path: string = CsvCast(newDoc.data, PDFCast(newDoc.data)).url.pathname;
        const { document_json } = await Networking.PostToServer('/createDocument', { file_path: local_file_path });
        this.documents.push(...document_json.map(convertToAIDocument));
        newDoc['ai_document'] = document_json;
    };

    @action
    toggleToolLogs = (index: number) => {
        this.expandedScratchpadIndex = this.expandedScratchpadIndex === index ? null : index;
    };

    initializeOpenAI() {
        const configuration: ClientOptions = {
            apiKey: process.env.OPENAI_KEY,
            dangerouslyAllowBrowser: true,
        };
        return new OpenAI(configuration);
    }

    onPassiveWheel = (e: WheelEvent) => {
        if (this._props.isContentActive()) {
            e.stopPropagation();
        }
    };

    @action
    askGPT = async (event: React.FormEvent<HTMLFormElement>): Promise<void> => {
        event.preventDefault();
        this.inputValue = '';

        const textInput = event.currentTarget.elements.namedItem('messageInput') as HTMLInputElement;
        const trimmedText = textInput.value.trim();

        if (trimmedText) {
            try {
                textInput.value = '';
                runInAction(() => {
                    this.history.push({ role: ASSISTANT_ROLE.USER, text: trimmedText });
                });
                const { response } = await Networking.PostToServer('/askAgent', { input: trimmedText });
                runInAction(() => {
                    this.history.push({ role: ASSISTANT_ROLE.ASSISTANT, text: response });
                });
                this.dataDoc.data = JSON.stringify(this.history);
            } catch (err) {
                console.error('Error:', err);
            }
        }
    };

    // @action
    // uploadLinks = async (linkedDocs: Doc[]) => {
    //     if (this.isInitializing) {
    //         console.log('Initialization in progress, upload aborted.');
    //         return;
    //     }
    //     const urls: string[] = linkedDocs.map(doc => CsvCast(doc.data, PDFCast(doc.data)).url.pathname);
    //     const csvUrls: string[] = urls.filter(url => url.endsWith('.csv'));
    //     console.log(this.assistantID, this.threadID, urls);

    //     await Networking.PostToServer('/uploadPDFs', { file_path: urls[0] });

    //     // linkedDocs.forEach((doc, i) => {
    //     //     doc[this.Document[Id] + '_ai_field_id'] = openaiFileIds[i];
    //     //     console.log('AI Field ID: ' + openaiFileIds[i]);
    //     // });

    //     // if (csvUrls.length > 0) {
    //     //     for (let i = 0; i < csvUrls.length; i++) {
    //     //         this.linkedCsvIDs.push(openaiFileIds[urls.indexOf(csvUrls[i])]);
    //     //     }
    //     //     console.log('linked csvs:' + this.linkedCsvIDs);
    //     //     await this.openai.beta.assistants.update(this.assistantID, {
    //     //         tools: [{ type: 'file_search' }, { type: 'code_interpreter' }],
    //     //         tool_resources: {
    //     //             file_search: {
    //     //                 vector_store_ids: [this.vectorStoreID],
    //     //             },
    //     //             code_interpreter: {
    //     //                 file_ids: this.linkedCsvIDs,
    //     //             },
    //     //         },
    //     //     });
    //     // }
    // };

    componentDidMount() {
        this._props.setContentViewBox?.(this);
        if (this.dataDoc.data) {
            try {
                const storedHistory = JSON.parse(StrCast(this.dataDoc.data));
                runInAction(() => {
                    this.history.push(
                        ...storedHistory.map((msg: AssistantMessage) => ({
                            role: msg.role,
                            text: msg.text,
                            follow_up_questions: msg.follow_up_questions,
                            citations: msg.citations,
                        }))
                    );
                });
            } catch (e) {
                console.error('Failed to parse history from dataDoc:', e);
            }
        }
        reaction(
            () => {
                const linkedDocs = LinkManager.Instance.getAllRelatedLinks(this.Document)
                    .map(d => DocCast(LinkManager.getOppositeAnchor(d, this.Document)))
                    .map(d => DocCast(d?.annotationOn, d))
                    .filter(d => d);
                return linkedDocs;
            },
            linked => this.linked_docs_to_add.push(...linked.filter(linkedDoc => !this.linked_docs_to_add.includes(linkedDoc)))
        );

        observe(
            // right now this skips during initialization which is necessary because it would be blank
            // However, it will upload the same link twice when it is
            this.linked_docs_to_add,
            change => {
                // observe pushes/splices on a user link DB 'data' field (should only happen for local changes)
                switch (change.type as any) {
                    case 'splice':
                        if ((change as any).addedCount > 0) {
                            // maybe check here if its already in the urls datadoc array so doesn't add twice
                            console.log((change as any).added as Doc[]);
                            ((change as any).added as Doc[]).forEach(doc => {
                                this.uploadNewDocument(doc);
                            });
                        }
                        // (change as any).removed.forEach((link: any) => remLinkFromDoc(toRealField(link)));
                        break;
                    case 'update': // let oldValue = change.oldValue;
                    default:
                }
            },
            true
        );
    }

    @action
    handleFollowUpClick = (question: string) => {
        console.log('Follow-up question clicked:', question);
        this.inputValue = question;
    };
    render() {
        return (
            /** <MathJaxContext config={this.mathJaxConfig}> **/
            <div className="chatBox">
                {this.isInitializing && <div className="initializing-overlay">Initializing...</div>}
                <div
                    className="scroll-box chat-content"
                    ref={r => {
                        this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel);
                        this._oldWheel = r;
                        r?.addEventListener('wheel', this.onPassiveWheel, { passive: false });
                    }}>
                    <div className="messages">
                        {
                            //this.history.map((message, index) => (
                            // <MessageComponent
                            //     key={index}
                            //     message={message}
                            //     toggleToolLogs={this.toggleToolLogs}
                            //     expandedLogIndex={this.expandedLogIndex}
                            //     index={index}
                            //     showModal={this.showModal}
                            //     goToLinkedDoc={() => {}}
                            //     setCurrentFile={this.setCurrentFile}
                            //     onFollowUpClick={this.handleFollowUpClick}
                            // />
                            //)
                            //)
                        }
                        {
                            //!this.current_message ? null : (
                            // <MessageComponent
                            //     key={this.history.length}
                            //     message={this.current_message}
                            //     toggleToolLogs={this.toggleToolLogs}
                            //     expandedLogIndex={this.expandedLogIndex}
                            //     index={this.history.length}
                            //     showModal={this.showModal}
                            //     goToLinkedDoc={() => {}}
                            //     setCurrentFile={this.setCurrentFile}
                            //     onFollowUpClick={this.handleFollowUpClick}
                            // />
                            //)
                        }
                    </div>
                </div>
                <form onSubmit={this.askGPT} className="chat-form">
                    <input type="text" name="messageInput" autoComplete="off" placeholder="Type a message..." value={this.inputValue} onChange={e => (this.inputValue = e.target.value)} />
                    <button type="submit">Send</button>
                </form>
            </div>
            /** </MathJaxContext> **/
        );
    }
}

Docs.Prototypes.TemplateMap.set(DocumentType.CHAT, {
    layout: { view: ChatBox, dataField: 'data' },
    options: { acl: '', chat: '', chat_history: '', chat_thread_id: '', chat_assistant_id: '', chat_vector_store_id: '' },
});
