import { Observable, Subscription } from 'rxjs';

import { WebExtHostDocumentsShape, Connection, createProxyAndHandleRequests, ExtHostContext } from '@workbench-stack/extension-api';
import { SubscriptionMap } from '../common';
import { MainThreadDocumentsAndEditors } from './client-document-and-editors';
import { shouldSynchronizeModel } from '@vscode/monaco-editor/esm/vs/editor/common/services/modelService';
import { ITextModel } from '@vscode/monaco-editor/esm/vs/editor/common/model';
import { URI } from 'cdecode/client';
import { ITextFileService  } from '@workbench-stack/core';


export class ClientDocuments {
    private _modelIsSynced = {};
    private _modelToDisposeMap = Object.create(null);

    private _serverProxy: any;
    private _proxy: WebExtHostDocumentsShape;
    private subscriptions = new Subscription();
    private registrations = new SubscriptionMap();

    constructor(
        connection: Connection,
        private readonly modelTextDocuments: ITextFileService,
        private readonly documentsAndEditors: MainThreadDocumentsAndEditors,
    ) {
        console.log('[check>>>] ClientDocuments created!!!');
        this._proxy = createProxyAndHandleRequests('documents', connection, this);
        this._serverProxy = createProxyAndHandleRequests(ExtHostContext.ExtHostDocuments.sid, connection, this);

        // this.subscriptions.add(
        //     modelTextDocuments.subscribe(docs => this._proxy.$acceptDocumentData(docs || [])),
        // );

        this.subscriptions.add(
            this.documentsAndEditors.onDocumentAdd.subscribe((models: any[]) => models.forEach(this._onModelAdded, this)),
        );

        this.subscriptions.add(
            this.documentsAndEditors.onDocumentRemove.subscribe((urls: any[]) => urls.forEach(this._onModelRemoved, this)),
        );

        this.subscriptions.add(this.registrations);
    }

    public unsubscribe(): void {
        this.subscriptions.unsubscribe();
    }

    private _onModelAdded(model: ITextModel): void {
        // Same filter as in mainThreadEditorsTracker
        console.log('[check>>>]: _onModelAdded', model, shouldSynchronizeModel(model));
        if (!shouldSynchronizeModel(model)) {
            // don't synchronize too large models
            return;
        }
        try {
            const modelUrl = model.uri;
            this._modelIsSynced[modelUrl.toString()] = true;
            this._modelToDisposeMap[modelUrl.toString()] = model.onDidChangeContent((e) => {
                this._proxy.$acceptModelChanged(modelUrl, e, this.modelTextDocuments.isDirty(modelUrl));
                this._serverProxy.$acceptModelChanged(modelUrl, e, this.modelTextDocuments.isDirty(modelUrl));
            });
        } catch (e) { console.error('[check>>>] error: ', e); }
    }

    private _onModelModeChanged(event: { model: ITextModel; oldModeId: string; }): void {
        let { model, oldModeId } = event;
        const modelUrl = model.uri;
        if (!this._modelIsSynced[modelUrl.toString()]) {
            return;
        }
        console.log('[check>>]: Model Mode Changed: ', event);
        // this._proxy.$acceptModelModeChanged(model.uri, oldModeId, model.getLanguageIdentifier().language);
    }

    private _onModelRemoved(modelUrl: URI): void {
        console.log('[check>>]: Model Removed: ', modelUrl);

        const strModelUrl = modelUrl.toString();
        if (!this._modelIsSynced[strModelUrl]) {
            return;
        }
        delete this._modelIsSynced[strModelUrl];
        this._modelToDisposeMap[strModelUrl].dispose();
        delete this._modelToDisposeMap[strModelUrl];

    }
}
