
import { URI } from '@vscode/monaco-editor/esm/vs/base/common/uri';
import { ApolloClient } from 'apollo-client';
import { Observable, of, Subscribable, from, combineLatest, forkJoin, merge, interval, timer, Subject, BehaviorSubject, MonoTypeOperatorFunction } from 'rxjs';
import { SaveFileChunk_WsMutation, SAVE_FILE_MUTATION, SaveFileChanges_WsMutationVariables } from '@files-stack/client-state';
import { ApolloServer } from 'apollo-server';
import { typeDefs, resolver } from '@files-stack/graphql-schema';
import { makeExecutableSchema } from 'graphql-tools';
import { logger } from '@cdm-logger/server';
import 'jest';
import { createTestClient } from 'apollo-server-testing';
import { TestFileService } from './test-file-service';
import * as monaco from '@vscode/monaco-editor/esm/vs/editor/editor.api';
import { TestScheduler } from 'rxjs/testing';
import * as paths from '@vscode/monaco-editor/esm/vs/base/common/path';
import { IFileService, FileChangesEvent, FileOperationEvent, IResourceEncodings, IFileStat, IResolveFileResult, IStreamContent, IContent } from '@files-stack/core';
import { ITextBufferFactory, ITextModel } from '@vscode/monaco-editor/esm/vs/editor/common/model';
import { StaticServices } from '@vscode/monaco-editor/esm/vs/editor/standalone/browser/standaloneServices';
import { IStringStream, ITextSnapshot } from '@vscode/monaco-editor/esm/vs/platform/files/common/files';
import { PieceTreeTextBufferBuilder } from '@vscode/monaco-editor/esm/vs/editor/common/model/pieceTreeTextBuffer/pieceTreeTextBufferBuilder';

import { MODEL_CONTENT_CHANGE, SAVE_RESPONSE, modelContentChangeEpic, ModelChangedContentActionType } from './model-epics';

export function createTextFileService(apolloClient: ApolloClient<any>) {

    function saveTextFile(resource: string, value: string, options: {}) {
        return apolloClient.mutate<SaveFileChunk_WsMutation, SaveFileChanges_WsMutationVariables>({
            mutation: SAVE_FILE_MUTATION,
            variables: {
                resource,
                value,
                options,
            },
        });
    }

    return {
        saveTextFile,
        getDirty: () => {
            return [];
        },
        isDirty(resource?: URI): boolean {
            return true;
        },
    };
}



describe('testFileService', () => {
    const rootSchema = `
    type Query {
      dummy: Int
    }
    type Mutation {
      dummy: Int
    }
    type Subscription {
        dummy: Int
    }
    `;


    const schemaTypeDef = rootSchema.concat(typeDefs);
    const resolvers = resolver({ logger });

    const schema = makeExecutableSchema({
        typeDefs: schemaTypeDef,
        resolvers,
    });

    const server = new ApolloServer({
        schema,
        mockEntireSchema: false,
        context: () => ({
            fileService: new TestFileService(),
        }),
    });
    const apolloClient: any = createTestClient(server);


    xtest('test fileService from client', async () => {
        const resource = 'file:///test.file';

        const textFileService = createTextFileService(apolloClient as any);

        const res = await textFileService.saveTextFile(resource as any, 'test', {});
        console.log('---res', res);
    });

    test('test file', async () => {

        const modeUriB = monaco.Uri.parse('b://b/foo.json');
        const modelUriA = monaco.Uri.parse('a://a/foo.json'); // a made up unique URI for our model

        const modelB = monaco.editor.createModel('', 'json', modeUriB);
        const modelA = monaco.editor.createModel('', 'json', modelUriA);

        const testScheduler = new TestScheduler((actual, expected) => {
            expect(actual).toMatchObject(expected);
        });

        const modelA2 = { getVersionId: () => 2, uri: 'a://a/foo.json' };
        const modelA3 = { getVersionId: () => 3, uri: 'a://a/foo.json' };
        const modelA4 = { getVersionId: () => 4, uri: 'a://a/foo.json' };
        const modelA5 = { getVersionId: () => 4, uri: 'a://a/foo.json' };
        const modelA6 = { getVersionId: () => 4, uri: 'a://a/foo.json' };


        testScheduler.run(({ hot, cold, expectObservable }) => {
            const action$ = hot<ModelChangedContentActionType>('-a-c-d--e-f-g-h', {
                a: { type: MODEL_CONTENT_CHANGE, payload: { model: modelA } },
                c: { type: MODEL_CONTENT_CHANGE, payload: { model: modelB } },
                d: { type: MODEL_CONTENT_CHANGE, payload: { model: modelA2 } },
                e: { type: MODEL_CONTENT_CHANGE, payload: { model: modelA3 } },
                f: { type: MODEL_CONTENT_CHANGE, payload: { model: modelA4 } },
                g: { type: MODEL_CONTENT_CHANGE, payload: { model: modelA5 } },
                h: { type: MODEL_CONTENT_CHANGE, payload: { model: modelA6 } },


            });
            const state$ = null;

            const dependencies = {
                apolloClient: {
                    mutate: ({ mutation, variables: { resource, update } }) => cold<{ data: IFileStat }>('--a|',
                        {
                            a: {
                                data: {
                                    resource: resource,
                                    etag: 'index.txt',
                                    // encoding: 'utf8',
                                    mtime: Date.now(),
                                    isDirectory: false,
                                    // name: paths.basename(URI.parse(resource).fsPath),
                                } as any,
                            },
                        }),
                    query: () => cold<{ data: any }>('-bbbb-|', {
                        a: {
                            data: {
                                files: {
                                    autoSaveDelay: 2000,
                                    autoSave: 'off',
                                },
                            },

                        },
                        b: {
                            data: {
                                files: {
                                    autoSaveDelay: 2000,
                                    autoSave: 'afterDelay',
                                },
                            },
                        },
                    }),
                },
            };
            const output$ = modelContentChangeEpic(action$ as any, state$, dependencies as any);
            expectObservable(output$).toBe('   -- 2014ms a ', {
                a: {
                    type: SAVE_RESPONSE,
                    data: {
                        etag: 'index.txt',
                    },
                },
            });

        });


    });

    function updateModel(model: ITextModel, value: string | ITextBufferFactory): void {
        const modelServiceStatic = StaticServices.modelService.get();
        modelServiceStatic.updateModel(model, value);
    }

    function createTextBufferBuilder() {
        return new PieceTreeTextBufferBuilder();
    }

    function createTextBufferFactoryFromSnapshot(snapshot: ITextSnapshot): ITextBufferFactory {
        let builder = createTextBufferBuilder();

        let chunk: string | null;
        while (typeof (chunk = snapshot.read()) === 'string') {
            builder.acceptChunk(chunk);
        }

        return builder.finish();
    }
    xtest('update modal with snapshot', () => {
        const modelUriA = monaco.Uri.parse('a://a/foo.json'); // a made up unique URI for our model
        const modelA: ITextModel = monaco.editor.createModel('', 'json', modelUriA) as any;
        console.log('---before update', modelA.getVersionId());
        const shot1 = modelA.createSnapshot();
        logger.info('---snapshot [%j]', createTextBufferFactoryFromSnapshot(shot1));
        modelA.setValue('hello');
        logger.info('---snapshot [%j]', createTextBufferFactoryFromSnapshot(modelA.createSnapshot()));

        updateModel(modelA as any, 'test 123');
        console.log('----after update', modelA.getVersionId());
        console.log('----get value', modelA.getValue());
        const shot2 = modelA.createSnapshot();
        logger.info('---snapshot [%j]', createTextBufferFactoryFromSnapshot(shot2));

        const modeUriB = monaco.Uri.parse('b://b/foo.json');
        const modelB = monaco.editor.createModel('', 'json', modeUriB);
        updateModel(modelB as any, 'test abc');

        expect(shot1).not.toEqual(shot2);
        expect(modelA.getValue()).toBe('test 123');
        expect(modelB.getValue()).toBe('test abc');

    });




});
