import { ContentBlockNode, HighlightNode } from '@/__generated__/graphql';
import { makeVar, InMemoryCache } from '@apollo/client/core';
import log from 'loglevel';

const showNavigationSidebarVar = makeVar(false);
const showProfileSidebarVar = makeVar(false);
const scrollToElementVar = makeVar('');
const currentFilterVar = makeVar('');
const helloEmailVar = makeVar('');
const languageVar = makeVar('de');

const idToRefFactory =
  (__typename: string) =>
  (_, { args, toReference }) => {
    log.debug(`Referencing ${__typename} with ${args.id}`);
    return toReference({
      __typename,
      id: args.id,
    });
  };

const typePolicies = {
  ProjectNode: {
    keyFields: ['slug'],
  },
  InstrumentNode: {
    keyFields: ['slug'],
    fields: {
      highlights: {
        merge(_existing: HighlightNode[], incoming: HighlightNode[]) {
          return incoming;
        },
      },
    },
  },
  RoomNode: {
    keyFields: ['slug'],
  },
  ChapterNode: {
    fields: {
      contentBlocks: {
        merge(_existing: ContentBlockNode[], incoming: ContentBlockNode[]) {
          // always replace the whole array
          return incoming;
        },
      },
    },
  },
  ModuleNode: {
    fields: {
      inEditMode: {
        read(previous: boolean | undefined) {
          return previous !== undefined ? previous : false;
        },
      },
      highlights: {
        merge(_existing: HighlightNode[], incoming: HighlightNode[]) {
          return incoming;
        },
      },
    },
    keyFields: ['slug'],
  },
  PrivateUserNode: {
    fields: {
      language: {
        read() {
          console.log(`returning language ${languageVar()}`);
          return languageVar();
        },
      },
    },
  },
  RoomEntryNode: {
    keyFields: ['slug'],
  },
  ContentBlockNode: {
    fields: {
      highlights: {
        merge(_existing: HighlightNode[], incoming: HighlightNode[]) {
          // always replace the whole array
          return incoming;
        },
      },
    },
  },
  // https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#example
  Query: {
    fields: {
      sidebar: {
        read() {
          return {
            profile: showProfileSidebarVar(),
            navigation: showNavigationSidebarVar(),
          };
        },
      },
      scrollPosition: {
        read() {
          return {
            scrollTo: scrollToElementVar(),
          };
        },
      },
      instrumentFilter: {
        read() {
          return {
            currentFilter: currentFilterVar(),
          };
        },
      },
      helloEmail: {
        read() {
          return {
            email: helloEmailVar(),
          };
        },
      },

      // these used to be in cacheRedirects, now here
      contentBlock: { read: idToRefFactory('ContentBlockNode') },
      chapter: { read: idToRefFactory('ChapterNode') },
      assignment: { read: idToRefFactory('AssignmentNode') },
      objective: { read: idToRefFactory('ObjectiveNode') },
      objectiveGroup: { read: idToRefFactory('ObjectiveGroupNode') },
      projectEntry: { read: idToRefFactory('ProjectEntryNode') },
    },
  },
};

const possibleTypes = {
  ContentBlockInterface: ['ContentBlockNode', 'SnapshotContentBlockNode'],
  ChapterInterface: ['ChapterNode', 'SnapshotChapterNode'],
  HighlightableNode: ['ContentBlockNode', 'InstrumentNode'],
};

const cache = new InMemoryCache({
  // used to 'override' the behavior in resolving different types. We use it for local state management
  // https://www.apollographql.com/docs/react/local-state/managing-state-with-field-policies/#example
  typePolicies,
  possibleTypes,
  // todo: document what this does, or link the doc for it at least
  // dataIdFromObject: obj => {
  //   switch (obj.__typename) {
  //     case 'InstrumentNode':
  //     case 'ModuleNode':
  //     case 'RoomEntryNode':
  //       return `${obj.__typename}:${obj.slug}`;
  //     default:
  //       return defaultDataIdFromObject(obj);
  //   }
  // },
});

// TODO: Monkey-patching in a fix for an open issue suggesting that
// `readQuery` should return null or undefined if the query is not yet in the
// cache: https://github.com/apollographql/apollo-feature-requests/issues/1
// probably not needed any more, as per https://github.com/apollographql/apollo-client/pull/7098
// cache.originalReadQuery = cache.readQuery;
// cache.readQuery = (...args) => {
//   try {
//     return cache.originalReadQuery(...args);
//   } catch (err) {
//     console.error(err);
//     return undefined;
//   }
// };

export {
  showProfileSidebarVar,
  showNavigationSidebarVar,
  scrollToElementVar,
  currentFilterVar,
  helloEmailVar,
  languageVar,
  cache,
};
export default cache;
