import { onError } from '@apollo/client/link/error';
import { ApolloClient, ApolloLink, createHttpLink } from '@apollo/client/core';
import { typeDefs } from '@/graphql/typedefs';
import { resolvers } from '@/graphql/resolvers';
import cache from './cache';
import log from 'loglevel';

import { router } from '@/router';

export default function (uri, networkErrorCallback) {
  const httpLink = createHttpLink({
    // uri: process.env.NODE_ENV !== 'production' ? 'http://localhost:8000/api/graphql/' : '/api/graphql/',
    uri,
    credentials: 'include',
    headers: {
      'X-CSRFToken': document.cookie.replace(/(?:(?:^|.*;\s*)csrftoken\s*=\s*([^;]*).*$)|^.*$/, '$1'),
    },
  });

  const consoleLink = new ApolloLink((operation, forward) => {
    log.debug('operation', operation.operationName);

    return forward(operation).map((data) => {
      log.debug(`returned from server for ${operation.operationName}`, data);
      return data;
    });
  });

  // from https://github.com/apollographql/apollo-client/issues/1564#issuecomment-357492659
  const omitTypename = (key, value) => {
    return key === '__typename' ? undefined : value;
  };

  const createOmitTypenameLink = new ApolloLink((operation, forward) => {
    if (operation.variables) {
      operation.variables = JSON.parse(JSON.stringify(operation.variables), omitTypename);
    }

    return forward(operation);
  });

  const errorLink = onError(({ networkError, graphQLErrors }) => {
    if (networkError && networkErrorCallback) {
      networkErrorCallback(networkError.statusCode);
    }

    if (graphQLErrors) {
      graphQLErrors.forEach(({ message, locations, path }) =>
        log.warn(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
      );
    }

    /*
      The server redirects to the HTML login page, but apollo expects a JSON response. This is fine, we'll just ignore it
     */
    if (networkError && networkError.name === 'ServerParseError') {
      // workaround found here: https://github.com/apollographql/apollo-link/issues/855#issuecomment-485764697
    }
  });

  const notFoundLink = new ApolloLink((operation, forward) => {
    return forward(operation).map((response) => {
      const { data } = response;
      if (data) {
        if (data.topic && data.topic.__typename === 'NotFound') {
          // redirect to general 404 page.
          // todo: specific topic not found page, with navigation?
          router.push({
            name: 'not-found',
          });
        }
      }
      return response;
    });
  });

  let composedLink;
  if (process.env.NODE_ENV === 'production') {
    composedLink = ApolloLink.from([createOmitTypenameLink, errorLink, notFoundLink, httpLink]);
  } else {
    composedLink = ApolloLink.from([consoleLink, createOmitTypenameLink, errorLink, notFoundLink, httpLink]);
  }

  // Create the apollo client
  return new ApolloClient({
    link: composedLink,
    cache,
    connectToDevTools: true,
    typeDefs,
    resolvers,
  });
}
