/* eslint-disable no-param-reassign */
import { createAction, createReducer } from '@reduxjs/toolkit';
import { createSelector } from 'reselect';
import { print } from 'graphql/language/printer';
import { connectToBackground } from '../messagesAndActionTypes/initializeActions';
import sendMessageTypes from '../messagesAndActionTypes/messageTypes';

/*---------------------------
 DATA OBJECT PARAMS:
-----------------------------
 FROM CLIENT APP
   Apollo Data Obj {
      manual: manualFetch,
      graphQlUri,
      queries: [queryDataObjs]
      mutations: [mutationDataObjs]
      cache: cacheInstance,
      queryCount: queryIdCounter,
      mutationCount: mutationIdCounter,
      prevQueryCount: queryCount,
      prevMutationCount: mutationCount,
    }

  Query Data Obj {
    id: `Q${key}${queryIdCounter}`, // prevents duplicate Ids in Epoch
    document: value.document,
    graphQLErrors: value.graphQLErrors,
    networkError: value.networkError,
    networkStatus: value.networkStatus,
    variables: value.variables,
    lastResult,    
  }

  Mutation Data Obj {
      id: `M${id}${mutationIdCounter}`, // prevents duplicate Ids in Epoch
      document: mutationObj.mutation,
      error: mutationObj.error,
      variables: mutationObj.variables,     
  }
  ----------------------------------------
  IN STATE
  queryObj: {
    id: (Q + cacheId + qCount)
    type,
    queryString,
    variables,
    response
    cacheSnapshot,
    diff,
}

  mutationsObj: {
    id: (M + cacheId + mCount)
    type,
    queryString,
    variables,
    cacheSnapshot,
    diff,
}

  manualFetchObj: {
    id: (F + fetchCounter)
    type,
    cacheSnapshot,
    diff,
}


-----------------------------*/

let superPort;
const queryType = 'Query';
const mutationType = 'Mutation';
const manualFetchType = 'Manual Fetch';

const initialState = {
  hasDunderApollo: false,
  activeQuery: {},
  chromeTabId: '',
  graphQlUri: '',
  queryIds: [],
  queries: {},
  queryIdCounter: 1,
  mutationIds: [],
  mutations: {},
  mutationIdCounter: 1,
  manualFetches: {}, // store manual cacheFetches in Timeline
  manualFetchIds: [],
  fetchCounter: 0,
  timeline: [], // an ordered list of query and mutation Ids
  typeNameDocumentCache: {},
};

/*--------------
  ACTION TYPES
----------------*/

const STARTING_UP = 'startingUp'; // For debugging
const PORT_INITIALIZED = 'portInitialized';
const POST_BACKGROUND_MESSAGE = 'callBackground';
const NO_APOLLO = 'noApolloClient';
const FETCH_APOLLO = 'fetchApolloData';
const RECEIVED_MANUAL_FETCH = 'apolloReceivedManual';
const RECEIVED_APOLLO = 'apolloReceived';
const INITIALIZED_CACHE_CHECK = 'initializedCache';
const SET_ACTIVE_QUERY = 'setActiveQuery';

/*----------------
  ACTION CREATORS
------------------*/
// eslint-disable-next-line import/prefer-default-export
export const startingUp = createAction(STARTING_UP); // no payload
export const initializedPort = createAction(PORT_INITIALIZED); // superPortObj
export const postBackgroundMessage = createAction(POST_BACKGROUND_MESSAGE); // messageObj: {type, payload}
export const noApollo = createAction(NO_APOLLO);
export const fetchApollo = createAction(FETCH_APOLLO); // should post message
export const receivedManualFetch = createAction(RECEIVED_MANUAL_FETCH); // payload apolloObj
export const receivedApollo = createAction(RECEIVED_APOLLO); // payload apolloObj
export const initializeCache = createAction(INITIALIZED_CACHE_CHECK);
export const setActiveQuery = createAction(SET_ACTIVE_QUERY); // payload should be an ID from the timeline

/*--------------
  REDUCER
----------------*/
const apolloReducer = createReducer(initialState, {
  [PORT_INITIALIZED]: initializePortCase,
  [POST_BACKGROUND_MESSAGE]: callBackgroundCase,
  [STARTING_UP]: startingUpCase,
  [NO_APOLLO]: noApolloCase,
  [FETCH_APOLLO]: fetchApolloCase,
  [RECEIVED_MANUAL_FETCH]: receivedManualFetchCase,
  [RECEIVED_APOLLO]: receivedApolloCase,
  [INITIALIZED_CACHE_CHECK]: initializedCacheCase,
  [SET_ACTIVE_QUERY]: setActiveQueryCase,
});

/*--------------
  REDUCER CASES
----------------*/
function startingUpCase(state, action) {
  console.log('Initializing Port');
}

function initializePortCase(state, action) {
  superPort = action.payload;
}

function callBackgroundCase(state, action) {
  superPort.connection.postMessage(action.payload);
}

function fetchApolloCase(state, action) {
  console.log('reduceTabId', chrome.devtools.inspectedWindow.tabId);
  superPort.connection.postMessage({
    type: sendMessageTypes.epoch.fetchApolloData,
    payload: chrome.devtools.inspectedWindow.tabId,
  });
}

function noApolloCase(state, action) {
  console.log('No Apollo Case');
  state.hasDunderApollo = false;
}

function receivedApolloCase(state, action) {
  const apolloData = action.payload;
  const {
    graphQlUri,
    queries,
    mutations,
    cache,
    queryCount,
    mutationCount,
    prevQueryCount,
    prevMutationCount,
  } = apolloData;

  state.hasDunderApollo = true;
  state.graphQlUri = graphQlUri;
  console.log(`CurrQ: ${queryCount}, PrevQ: ${prevQueryCount}, StateQ: ${state.queryIdCounter}`);
  console.log(
    `CurrM: ${mutationCount}, PrevM: ${prevMutationCount}, StateM: ${state.mutationIdCounter}`
  );

  // Debug
  if (state.queryIdCounter !== prevQueryCount || state.mutationIdCounter !== prevMutationCount) {
    console.log('*****QUERIES / MUTATIONS MISSED*****');
    console.log(`CurrQ: ${queryCount}, PrevQ: ${prevQueryCount}, StateQ: ${state.queryIdCounter}`);
    console.log(
      `CurrM: ${mutationCount}, PrevM: ${prevMutationCount}, StateM: ${state.mutationIdCounter}`
    );
  }

  let mutationsToGrab = mutationCount - prevMutationCount;
  if (mutationsToGrab && mutationsToGrab <= mutations.length) {
    while (mutationsToGrab > 0) {
      const { id, document, error, variables } = mutations.pop();
      const stateMutationObj = {
        id,
        type: mutationType,
        queryString: print(document),
        variables,
        cacheSnapshot: cache,
        diff: 'Magic Diff Formula Magic Result Inserted Here',
      };
      state.timeline.push(id);
      state.mutationIds.push(id);
      state.mutations[id] = stateMutationObj;
      mutationsToGrab -= 1;
    }
  }
  state.mutationIdCounter = mutationCount;

  let queriesToGrab = queryCount - prevQueryCount;
  if (queriesToGrab && queriesToGrab <= queries.length) {
    while (queriesToGrab > 0) {
      console.log('received Q Case queries ->', queries);
      console.log('query items ->', queries.length);
      const queryObj = queries.pop();
      console.log('q in question -> ', queryObj);
      const { id, document, lastResult, variables } = queryObj;
      const stateQueryObj = {
        id,
        type: queryType,
        queryString: print(document),
        variables,
        response: lastResult,
        cacheSnapshot: cache,
        diff: 'Magic Diff Formula Magic Result Inserted Here',
      };
      state.timeline.push(id);
      state.queryIds.push(id);
      state.queries[id] = stateQueryObj;
      queriesToGrab -= 1;
    }
  }
  state.queryIdCounter = queryCount;
}

function receivedManualFetchCase(state, action) {
  const apolloData = action.payload;
  state.hasDunderApollo = true;
  state.fetchCounter += 1;
  const fetchId = `F${state.fetchCounter}`;
  const diff = 'Magic Diff Formula Magic Result Inserted Here';
  const fetchObj = {
    id: fetchId,
    type: manualFetchType,
    cacheSnapshot: apolloData.cache,
    diff,
  };
  state.timeline.push(fetchId);
  state.manualFetchIds.push(fetchId);
  state.manualFetches[fetchId] = fetchObj;
}

function initializedCacheCase(state, action) {
  console.log('Initialized Cache Case Fired');
}

function setActiveQueryCase(state, action) {
  const activeId = action.payload;
  if (!activeId) return;
  const typeIndicator = activeId[0];
  if (typeIndicator === 'Q') state.activeQuery = state.queries[activeId];
  if (typeIndicator === 'M') state.activeQuery = state.mutations[activeId];
  if (typeIndicator === 'F') state.activeQuery = state.manualFetches[activeId];
}

export default apolloReducer;

/*--------------------
  ACTION GENERATORS
--------------------*/

// Returns function that, when called creates an connectToBackground action object passing
// in the following action types for the resulting super port to dispatch
export const initializeBackgroundConnection = () =>
  connectToBackground({
    onStart: STARTING_UP,
    onSuccess: PORT_INITIALIZED,
    apolloActions: {
      receivedApollo: RECEIVED_APOLLO,
      receivedApolloManual: RECEIVED_MANUAL_FETCH,
      noApollo: NO_APOLLO,
      initializeCache: INITIALIZED_CACHE_CHECK,
    },
  });

/*-----------
  SELECTORS
-------------*/
export const getTimeline = createSelector(
  (state) => {
    console.log('selector State', state);
    return state.apollo.timeline;
  },
  (state) => state.apollo.queries,
  (state) => state.apollo.mutations,
  (state) => state.apollo.manualFetches,
  (timeline, queries, mutations, manualFetches) => {
    return timeline.map((id) => {
      const typeIndicator = id[0];
      if (typeIndicator === 'Q') return queries[id];
      if (typeIndicator === 'M') return mutations[id];
      return manualFetches[id];
    });
  }
);

/*--------------
HELPERS
---------------*/
function createQueryString(document) {
  return print(document);
}