import {
  ApolloClient,
  InMemoryCache,
  Observable,
  createHttpLink,
  from,
} from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { RestLink } from 'apollo-link-rest';
import { onError } from '@apollo/client/link/error';
import { refreshAccessToken } from './services/AuthService';

const apiLink = createHttpLink({
  uri: `${process.env.REACT_APP_API_URL}/v1/graphql`,
});

const withAuthHeadersLink = setContext((_, { headers }) => {
  const token = localStorage.getItem('accessToken');
  return {
    headers: {
      ...headers,
      authorization: token ? `Bearer ${token}` : '',
    },
  };
});

const errorLink = onError(({ graphQLErrors, operation, forward }) => {
  if (graphQLErrors) {
    for (const err of graphQLErrors) {
      switch (err.extensions.code) {
        case 'invalid-jwt':
          return new Observable((observer) => {
            (async () => {
              try {
                await refreshAccessToken();

                const oldHeaders = operation.getContext().headers;

                operation.setContext({
                  headers: {
                    ...oldHeaders,
                    authorization: `Bearer ${localStorage.getItem(
                      'accessToken',
                    )}`,
                  },
                });

                const subscriber = {
                  next: observer.next.bind(observer),
                  error: observer.error.bind(observer),
                  complete: observer.complete.bind(observer),
                };

                forward(operation).subscribe(subscriber);
              } catch (error) {
                observer.error(error);
              }
            })();
          });
      }
    }
  }
});

export const apiClient = new ApolloClient({
  link: from([errorLink, withAuthHeadersLink, apiLink]),
  cache: new InMemoryCache(),
});

const authLink = new RestLink({
  uri: `${process.env.REACT_APP_AUTH_URL}`,
  credentials: 'same-origin',
});

export const authClient = new ApolloClient({
  link: authLink,
  cache: new InMemoryCache(),
});
