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 'jest';
// 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');

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('triggers new result from subscription data', done => {
        let latestResult: any = null;
        const wSLink = mockObservableLink();
        const httpLink = mockSingleLink(req1);


        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 req1['result']['data']>({
            query,
        });

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

        obsHandle.subscribeToMore<SubscriptionData>({
            document: gql`
            subscription newValues {
                name
            }
            `,
            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]);
        }

    });

    it('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]);
        }
    });
});

