import 'reflect-metadata';
import gql from 'graphql-tag';
import { ApolloLink, Operation } from 'apollo-link';
import { InMemoryCache } from 'apollo-cache-inmemory';
import { stripSymbols } from 'apollo-utilities';
import { DocumentNode, OperationDefinitionNode } from 'graphql';
import { ApolloClient } from 'apollo-client';
import { mockSingleLink, mockObservableLink } from '../__mocks__/mockLinks';
import { FileStreamContent_WsDocument, IFileStreamContent_WsQuery , IFileStreamContentSubscription, IFileStreamContentSubscriptionVariables } from '@files-stack/client-state';
import FilesModule from '@files-stack/module-server';
import * as rootSchemaDef from '../../../../../servers/backend-server/src/api/root-schema.graphqls';
import 'jest';
import { makeExecutableSchema } from 'graphql-tools';
import { resolver } from '../../../../workbench-module/server/src/graphql/resolvers';
import { logger } from '@cdm-logger/server';
import { filesModule } from '@files-stack/server-core';

// https://github.com/apollographql/apollo-client/blob/master/packages/apollo-client/src/__tests__/subscribeToMore.ts
const isSub = (operation: Operation) =>
    (operation.query as DocumentNode).definitions
        .filter(x => x.kind === 'OperationDefinition')
        .some((x: OperationDefinitionNode) => x.operation === 'subscription');

xdescribe('subscribeToMore', () => {
    const query = FileStreamContent_WsDocument;
    const variables = { resource: 'test'};
    const result = (name) => ({
        data: {
            resolveStreamContent: {
                resource: 'file:///test',
                name,
                size: null,
                mtime: 1534910384000,
                etag: 'dsfsf',
                value: 'test',
                encoding: 'utf8',
                isReadonly: false,
            },
        },
    });
    const resultTest = {
        data: {
            resolveStreamContent: {
                resource: 'file:///test',
                name: 'test',
                size: null,
                mtime: 1534910384000,
                etag: 'dsfsf',
                value: 'test',
                encoding: 'utf8',
                isReadonly: false,
            },
        },
    };
    const req1Result = result('req1');
    const req1 = { request: { query } as Operation, result: req1Result };

    const results = ['file1', 'file2'].map(name => ({
        result: resultTest,
        delay: 10,
    }));

    // const results2Gen = result('result2');
    // const results2 = [
    //     { result: results2Gen, delay: 10 },
    //     { error: new Error('You cant touch this'), delay: 10 },
    // ];

    // const results3Gen = result('result3');
    // const results3 = [
    //     { error: new Error('You cant touch this'), delay: 10 },
    //     { result: results3Gen, delay: 10 },
    // ];

    // const results4Gen = result('result4');
    // const result4 = {
    //     data: {
    //         entry: [{ value: '1' }, { value: '2' }],
    //     },
    // };
    // const req4 = { request: { query } as Operation, result: result4 };

    type SubscriptionData = IFileStreamContentSubscription;

    // xit('allows specification of custom types for variables and payload (#4246)', done => {
    //     interface TypedOperation extends Operation {
    //         variables: {
    //             someNumber: number;
    //         };
    //     }
    //     const typedReq = {
    //         request: { query, variables: { someNumber: 1 } } as TypedOperation,
    //         result,
    //     };
    //     interface TypedSubscriptionVariables {
    //         someString: string;
    //     }

    //     let latestResult: any = null;
    //     const wSLink = mockObservableLink();
    //     const httpLink = mockSingleLink(typedReq);

    //     const link = ApolloLink.split(isSub, wSLink, httpLink);
    //     let counter = 0;

    //     const client = new ApolloClient({
    //         cache: new InMemoryCache({ addTypename: false }),
    //         link,
    //     });

    //     const obsHandle = client.watchQuery<
    //         typeof typedReq['result']['data'],
    //         typeof typedReq['request']['variables']
    //     >({
    //         query,
    //         variables: { someNumber: 1 },
    //     });

    //     const sub = obsHandle.subscribe({
    //         next(queryResult) {
    //             latestResult = queryResult;
    //             counter++;
    //         },
    //     });

    //     obsHandle.subscribeToMore<SubscriptionData, TypedSubscriptionVariables>({
    //         document: gql`
    //         subscription newValues {
    //           name
    //         }
    //       `,
    //         variables: {
    //             someString: 'foo',
    //         },
    //         updateQuery: (_, { subscriptionData }) => {
    //             return { entry: { value: subscriptionData.data.name } };
    //         },
    //     });

    //     setTimeout(() => {
    //         sub.unsubscribe();
    //         expect(counter).toBe(3);
    //         expect(stripSymbols(latestResult)).toEqual({
    //             data: { entry: { value: 'Amanda Liu' } },
    //             loading: false,
    //             networkStatus: 7,
    //             stale: false,
    //         });
    //         done();
    //     }, 15);

    //     for (let i = 0; i < 2; i++) {
    //         wSLink.simulateResult(results[i]);
    //     }
    // });
});

describe('subscribeToMore', () => {
    const query = gql`
    query aQuery {
        entry {
            value
        }
    }
    `;

    const result = {
        data: {
            entry: {
                value: '1',
            },
        },
    };

    const req1 = { request: { query } as Operation, result };

    const results = ['Dahivat Pandya', 'Amanda Liu'].map(name => ({
        result: { data: { name } },
        delay: 10,
    }));

    const results2 = [
        { result: { data: { name: 'Amanda Liu' } }, delay: 10 },
        { error: new Error('You cant touch this'), delay: 10 },
    ];

    const results3 = [
        { error: new Error('You cant touch this'), delay: 10 },
        { result: { data: { name: 'Amanda Liu' } }, delay: 10 },
    ];

    const result4 = {
        data: {
            entry: [{ value: '1' }, { value: '2' }],
        },
    };
    const req4 = { request: { query } as Operation, result: result4 };

    interface SubscriptionData {
        name: string;
    }

    it('allows specification of custom types for variables and payload (#4246)', done => {

        const typeDefs = [rootSchemaDef].concat(FilesModule.schemas);
        const schema = makeExecutableSchema({ typeDefs, resolvers: FilesModule.createResolvers({ logger, pubsub: null })});
        console.log('--gen schema', schema);
        interface TypedOperation extends Operation {
            variables: {
                someNumber: number;
            };
        }
        const typedReq = {
            request: { query, variables: { someNumber: 1 } } as TypedOperation,
            result,
        };
        interface TypedSubscriptionVariables {
            someString: string;
        }

        let latestResult: any = null;
        const wSLink = mockObservableLink();
        const httpLink = mockSingleLink(typedReq);

        const link = ApolloLink.split(isSub, wSLink, httpLink);
        let counter = 0;

        const client = new ApolloClient({
            cache: new InMemoryCache({ addTypename: false }),
            link,
        });

        const obsHandle = client.watchQuery<
            typeof typedReq['result']['data'],
            typeof typedReq['request']['variables']
        >({
            query,
            variables: { someNumber: 1 },
        });

        const sub = obsHandle.subscribe({
            next(queryResult) {
                latestResult = queryResult;
                counter++;
            },
        });

        obsHandle.subscribeToMore<SubscriptionData, TypedSubscriptionVariables>({
            document: gql`
            subscription newValues {
              name
            }
          `,
            variables: {
                someString: 'foo',
            },
            updateQuery: (_, { subscriptionData }) => {
                return { entry: { value: subscriptionData.data.name } };
            },
        });

        setTimeout(() => {
            sub.unsubscribe();
            expect(counter).toBe(3);
            expect(stripSymbols(latestResult)).toEqual({
                data: { entry: { value: 'Amanda Liu' } },
                loading: false,
                networkStatus: 7,
                stale: false,
            });
            done();
        }, 15);

        for (let i = 0; i < 2; i++) {
            wSLink.simulateResult(results[i]);
        }
    });
});
