import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './store';
import { MultiGraph } from 'graphology';
import { QueryMultiGraphology as QueryGraphology } from '../../querybuilder/model/graphology/utils';
import { isEqual } from 'lodash-es';
import {
  Graph,
  Query,
  QueryBuilderSettings,
  GraphQueryTranslationResultMessage,
  QueryGraphEdgeHandle,
  QueryMultiGraph,
  QueryUnionType,
} from 'ts-common';

const defaultGraph: () => Graph = () => ({ nodes: [], edges: [], attributes: {}, options: {} });

export type QueryBuilderAttributeBeingShown = object;

export type QueryBuilderState = Query & {
  ignoreReactivity: boolean;
  queryTranslation: GraphQueryTranslationResultMessage;
};

export type SchemaState = {
  id?: number;
  settings: Record<string, any>;
};

// Define the initial state using that type
export const initialState: QueryBuilderState = {
  graph: defaultGraph(),
  ignoreReactivity: false,
  settings: {
    limit: 500,
    depth: { min: 1, max: 1 },
    layout: 'manual',
    autocompleteRelation: true,
    unionTypes: {},
  },
  queryTranslation: {
    queryID: '',
    result: '',
  },
  attributesBeingShown: [],
  // schemaLayout: 'Graphology_noverlap',
} as QueryBuilderState;

export const querybuilderSlice = createSlice({
  name: 'querybuilder',
  initialState,
  reducers: {
    setQuerybuilderGraph: (state: QueryBuilderState, action: PayloadAction<QueryMultiGraph>) => {
      state.graph = action.payload;
      state.ignoreReactivity = false;
    },
    /**
     * Sets the querybuilder nodes, settings, and attributes being shown,
     * if the payload contains the required information.
     * @param {QueryBuilderState} action.payload the payload with the new state
     */
    setQuerybuilderNodes: (state: QueryBuilderState, action: PayloadAction<Query>) => {
      if (action.payload.graph?.nodes && action.payload.graph?.edges) {
        state.graph = action.payload.graph;
        state.settings = action.payload.settings;
        state.attributesBeingShown = action.payload.attributesBeingShown || [];
        // state.ignoreReactivity = true;
      }
    },
    clearQB: (state: QueryBuilderState) => {
      state.graph = defaultGraph();
    },
    setQuerybuilderSettings: (state: QueryBuilderState, action: PayloadAction<QueryBuilderSettings>) => {
      state.settings = action.payload;
    },
    setQueryText: (state: QueryBuilderState, action: PayloadAction<GraphQueryTranslationResultMessage>) => {
      state.queryTranslation = action.payload;
    },
    attributeShownToggle: (state: QueryBuilderState, action: PayloadAction<QueryGraphEdgeHandle>) => {
      const existing = state.attributesBeingShown.findIndex(a => isEqual(a, action.payload));
      if (existing === -1) {
        state.attributesBeingShown.push(action.payload);
      } else {
        state.attributesBeingShown.splice(existing, 1);
      }
    },
    setQueryUnionType: (state: QueryBuilderState, action: PayloadAction<{ nodeId: string; unionType: QueryUnionType }>) => {
      if (state.settings.unionTypes == null) {
        state.settings.unionTypes = {};
      }
      state.settings.unionTypes[action.payload.nodeId] = action.payload.unionType;
    },
  },
});

export const queryBuilderState = (state: RootState): QueryBuilderState => state.querybuilder;
export const queryBuilderSettingsState = (state: RootState): QueryBuilderSettings => state.querybuilder.settings;

export const setQuerybuilderGraphology = (payload: QueryGraphology) => {
  return querybuilderSlice.actions.setQuerybuilderGraph(payload.export());
};

/** Select the querybuilder nodes in serialized fromat */
export const toQuerybuilderGraphology = (graph: QueryMultiGraph): QueryGraphology => {
  const ret = new QueryGraphology();
  ret.import(MultiGraph.from(graph).export());
  return ret;
};

/** Select the querybuilder nodes and convert it to a graphology object */
export const selectQuerybuilderGraph = (state: RootState): QueryMultiGraph => {
  // This is really weird but for some reason all the attributes appeared as read-only otherwise
  return state.querybuilder.graph as QueryMultiGraph;
};

/** Select the querybuilder nodes and convert it to a graphology object */
export const selectQuerybuilderHash = (state: RootState): string => {
  const hashedNodes = state.querybuilder.graph.nodes.map(n => {
    const node = { ...n };
    if (n?.attributes) {
      const newAttributes = { ...n?.attributes };
      newAttributes.x = 0;
      newAttributes.y = 0;
      newAttributes.height = 0;
      newAttributes.width = 0;
      node.attributes = newAttributes;
    }
    return node;
  });

  return JSON.stringify({
    nodes: hashedNodes,
    edges: state.querybuilder.graph.edges.map(n => ({
      key: n.key,
      source: n.source,
      target: n.target,
    })),
  });
};

// /**
//  * selects the SchemaLayout enum
//  * @param {GraphLayout} state
//  * @returns {GraphLayout} enum of type GraphLayout
//  */
// export const selectSchemaLayout = (state: RootState) =>
//   state.schema.schemaLayout;

export default querybuilderSlice.reducer;
export const {
  setQuerybuilderGraph,
  clearQB,
  setQuerybuilderSettings,
  setQuerybuilderNodes,
  setQueryText,
  attributeShownToggle,
  setQueryUnionType,
} = querybuilderSlice.actions;

export const queryBuilderAttributesShown = (state: RootState) => state.querybuilder.attributesBeingShown;