import {
  ApolloClient,
  ApolloLink,
  split,
  from,
  HttpLink,
  InMemoryCache,
} from '@apollo/client';
import { GraphQLWsLink } from '@apollo/client/link/subscriptions';
import { getMainDefinition } from '@apollo/client/utilities';
import { onError } from '@apollo/client/link/error';
import { RetryLink } from '@apollo/client/link/retry';
import { createClient as createWSClient } from 'graphql-ws';

export function createClient(base?: string) {
  if (!base) {
    throw new Error('Base must be passed into createClient!');
  }
  const gqlPath = 'query';
  const urlHTTP = new URL(gqlPath, base);
  const urlWS = new URL(gqlPath, base);
  // Replace http with ws, preserving if its a secure connection eg. https => wss
  urlWS.protocol = urlWS.protocol.replace('http', 'ws');

  const cache = new InMemoryCache({
    typePolicies: {
      Account: {
        keyFields: false,
        fields: {
          balanceFormatted: {},
        },
      },
      Instrument: {
        keyFields: false,
      },
      MarketData: {
        keyFields: ['market', ['id']],
      },
      Node: {
        keyFields: false,
      },
      Withdrawal: {
        fields: {
          pendingOnForeignChain: {
            read: (isPending = false) => isPending,
          },
        },
      },
    },
  });

  const retryLink = new RetryLink({
    delay: {
      initial: 300,
      max: 10000,
      jitter: true,
    },
  });

  const httpLink = new HttpLink({
    uri: urlHTTP.href,
    credentials: 'same-origin',
  });

  const wsLink = process.browser
    ? new GraphQLWsLink(
        createWSClient({
          url: urlWS.href,
        })
      )
    : new ApolloLink((operation, forward) => forward(operation));

  const splitLink = process.browser
    ? split(
        ({ query }) => {
          const definition = getMainDefinition(query);
          return (
            definition.kind === 'OperationDefinition' &&
            definition.operation === 'subscription'
          );
        },
        wsLink,
        httpLink
      )
    : httpLink;

  const errorLink = onError(({ graphQLErrors, networkError }) => {
    console.log(graphQLErrors);
    console.log(networkError);
  });

  return new ApolloClient({
    connectToDevTools: process.env['NODE_ENV'] === 'development',
    link: from([errorLink, retryLink, splitLink]),
    cache,
  });
}
