
import { Observable, Subject } from 'rxjs';
import { TextDocument as TextDocumentOriginal } from 'cdecode/client';
import { ITextDocumentItem, WebExtHostDocumentsShape } from '@workbench-stack/extension-api';
import { URI, UriComponents } from '@vscode/monaco-editor/esm/vs/base/common/uri';

type TextDocument = Pick<TextDocumentOriginal, 'languageId'> & { text: string };
export class ExtHostDocuments implements WebExtHostDocumentsShape {
    private documents = new Map<string, ITextDocumentItem>();

    constructor(private sync: () => Promise<void>) { }

    /**
     * Returns the known document with the given URI.
     *
     * @internal
     */
    public get(resource: string | UriComponents): TextDocument {
        let doc;
        if (typeof resource === 'string') {
            doc = this.documents.get(resource);
        } else {
            const uri = URI.revive(resource);
            doc = this.documents.get(uri.toString());
        }
        if (!doc) {
            throw new Error(`document not found: ${resource}`);
        }
        return doc;
    }

    /**
     * If needed, perform a sync with the client to ensure that its pending sends have been received before
     * retrieving this document.
     *
     * @todo This is necessary because hovers can be sent before the document is loaded, and it will cause a
     * "document not found" error.
     */
    public async getSync(resource: string | UriComponents): Promise<TextDocument> {
        let doc;
        if (typeof resource === 'string') {
            doc = this.documents.get(resource);
        } else {
            const uri = URI.revive(resource);
            doc = this.documents.get(uri.toString());
        }
        if (doc) {
            return doc;
        }
        await this.sync();
        return this.get(resource);
    }

    /**
     * Returns all known documents.
     *
     * @internal
     */
    public getAll(): TextDocument[] {
        return Array.from(this.documents.values());
    }

    private textDocumentAdds = new Subject<TextDocument>();
    public readonly onDidOpenTextDocument: Observable<TextDocument> = this.textDocumentAdds;

    public $acceptDocumentData(docs: ITextDocumentItem[] | null): void {
        if (!docs) {
            // We don't ever (yet) communicate to the extension when docs are closed.
            return;
        }
        for (const doc of docs) {
            console.log('-----doc URI', doc.uri);
            const uri = URI.revive(doc.uri);
            const isNew = !this.documents.has(uri.toString());
            this.documents.set(uri.toString(), doc);
            if (isNew) {
                this.textDocumentAdds.next(doc);
            }
        }
    }
}
