import { ApolloClient, ApolloLink, InMemoryCache } from "@apollo/client";
import { setContext } from "@apollo/client/link/context";
import { onError } from "@apollo/client/link/error";
import { HttpLink } from "@apollo/client/link/http"; //"apollo-link-http";
import { RetryLink } from "@apollo/client/link/retry";
import apolloLogger from "apollo-link-logger";
import { SentryLink } from "apollo-link-sentry";
import { auth } from "../firebase/firebase.utils";

const errorLink = onError(({ graphQLErrors, networkError, operation, forward }) => {
  if (graphQLErrors)
    graphQLErrors.forEach(({ message, locations, path }) =>
      console.log(`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`)
    );
  if (networkError) console.log(`[Network error]: ${JSON.stringify(networkError)}`);
  console.log(operation.getContext());
});

const httpLink = new HttpLink({
  uri: import.meta.env.VITE_APP_GRAPHQL_ENDPOINT
});

// const wsLink = new WebSocketLink({
//   uri: import.meta.env.VITE_APP_GRAPHQL_ENDPOINT_WS,
//   options: {
//     lazy: true,
//     reconnect: true,
//     connectionParams: async () => {
//       const token = auth.currentUser && (await auth.currentUser.getIdToken(true));
//       if (token) {
//         return {
//           headers: {
//             authorization: token ? `Bearer ${token}` : ""
//           }
//         };
//       }
//     }
//   }
// });

// const subscriptionMiddleware = {
//   applyMiddleware: async (options, next) => {
//     options.authToken = auth.currentUser && (await auth.currentUser.getIdToken(true));
//     next();
//   }
// };
// wsLink.subscriptionClient.use([subscriptionMiddleware]);

// const link = new HttpLink.split(
//   // split based on operation type
//   ({ query }) => {
//     const definition = getMainDefinition(query);
//     // console.log(
//     //   "##Intercepted GQL Transaction : " +
//     //     definition.operation +
//     //     "|" +
//     //     definition.name.value +
//     //     "##",
//     //   query
//     // );
//     return definition.kind === "OperationDefinition" && definition.operation === "subscription";
//   },
//   wsLink,
//   httpLink
// );

const authLink = setContext((_, { headers }) => {
  return (
    auth.currentUser &&
    auth.currentUser.getIdToken().then((token) => {
      if (token) {
        return {
          headers: {
            ...headers,
            authorization: token ? `Bearer ${token}` : ""
          }
        };
      } else {
        console.log("We have no authorization header.");
        return { headers };
      }
    })
  );
});

const retryLink = new RetryLink({
  delay: {
    initial: 500,
    max: 5,
    jitter: true
  },
  attempts: {
    max: 5,
    retryIf: (error, _operation) => !!error
  }
});

const sentryLink = new SentryLink();

const middlewares = [];
if (import.meta.env.DEV) {
  middlewares.push(apolloLogger);
}

middlewares.push(sentryLink.concat(retryLink.concat(errorLink.concat(authLink.concat(httpLink)))));

const mergeByOffset = (existing = [], incoming = [], { args }) => {
  const merged = existing ? existing.slice(0) : [];
  const offset = args?.offset || 0;

  incoming.forEach((item, index) => {
    merged[offset + index] = item;
  });

  return merged;
};

const cache = new InMemoryCache({
  typePolicies: {
    Query: {
      fields: {
        jobs: {
          keyArgs: ["order"],
          merge: mergeByOffset
        },
        search_jobs: {
          keyArgs: ["args", "where"],
          merge: mergeByOffset
        }
      }
    }
  }
});

export default new ApolloClient({
  link: ApolloLink.from(middlewares),
  cache,
  connectToDevTools: process.env.NODE_ENV !== "production",
  defaultOptions: {
    query: {
      fetchPolicy: "network-only"
    },
    watchQuery: {
      fetchPolicy: "network-only"
    }
  }
});
