import { createSlice, PayloadAction } from '@reduxjs/toolkit';
import type { RootState } from './store';
import { DatabaseStatus } from '../broker/wsState';
import { getParam, setParam, URLParams } from '../api/url';
import { cloneDeep } from 'lodash-es';
import { SaveState, SaveStateAuthorizationHeaders } from 'ts-common';

/** Message format of the error message from the backend */
export type ErrorMessage = {
  errorStatus: any;
  queryID: any;
};

/** Cache type */
export type SessionCacheI = {
  currentSaveState?: string; // id of the current save state
  saveStates: Record<string, SaveState>;
  saveStatesAuthorization: Record<string, SaveStateAuthorizationHeaders>;
  fetchingSaveStates: boolean;
  testedSaveState: Record<string, DatabaseStatus>;
};

const newStateAuthorizationHeaders: SaveStateAuthorizationHeaders = {
  query: { W: true, R: true },
  database: { W: true, R: true },
  visualization: { W: true, R: true },
  schema: { W: true, R: true },
  savestate: { W: true, R: true },
};

// Define the initial state using that type
export const initialState: SessionCacheI = {
  currentSaveState: undefined,
  saveStates: {},
  saveStatesAuthorization: {},
  fetchingSaveStates: true, // default to true to prevent flashing of the UI
  testedSaveState: {},
};

export const sessionSlice = createSlice({
  name: 'session',
  initialState: initialState,
  reducers: {
    setFetchingSaveStates: (state: SessionCacheI, action: PayloadAction<boolean>) => {
      state.fetchingSaveStates = action.payload;
    },
    selectSaveState: (state: SessionCacheI, action: PayloadAction<string | undefined>) => {
      if (action.payload && action.payload in state.saveStates) {
        state.currentSaveState = action.payload;
      } else {
        state.currentSaveState = undefined;
      }
      setParam(URLParams.saveState, action.payload);
    },
    updateSelectedSaveState: (state: SessionCacheI, action: PayloadAction<SaveState>) => {
      if (state.currentSaveState === action.payload.id && state.currentSaveState) state.saveStates[state.currentSaveState] = action.payload;
    },
    updateSaveStateList: (state: SessionCacheI, action: PayloadAction<SaveState[]>) => {
      // Does NOT clear the states, just adds in new data
      const newState: Record<string, SaveState> = {};
      if (!action.payload) return;
      action.payload.forEach((ss: SaveState) => {
        newState[ss.id] = ss;
      });
      state.saveStates = { ...state.saveStates, ...newState };
      const paramSaveState = getParam(URLParams.saveState);
      if (!state.currentSaveState) {
        if (paramSaveState && paramSaveState in state.saveStates) {
          state.currentSaveState = paramSaveState;
        } else if (Object.keys(state.saveStates).length > 0) {
          state.currentSaveState = Object.keys(state.saveStates)[0];
        } else state.currentSaveState = undefined;
      }
    },
    setSaveStateList: (state: SessionCacheI, action: PayloadAction<SaveState[]>) => {
      // Clears the states and puts in new data
      const newState: Record<string, SaveState> = {};
      action.payload.forEach(ss => {
        newState[ss.id] = ss;
      });
      state.saveStates = newState;
      const paramSaveState = getParam(URLParams.saveState);
      if (!state.currentSaveState) {
        if (paramSaveState && paramSaveState in state.saveStates) {
          state.currentSaveState = paramSaveState;
        } else if (Object.keys(state.saveStates).length > 0) {
          state.currentSaveState = Object.keys(state.saveStates)[0];
        } else state.currentSaveState = undefined;
      }
    },
    addSaveState: (state: SessionCacheI, action: PayloadAction<SaveState>) => {
      if (state.saveStates === undefined) state.saveStates = {};
      state.saveStates[action.payload.id] = action.payload;
      state.currentSaveState = action.payload.id;
      if (!state.saveStatesAuthorization[action.payload.id]) {
        state.saveStatesAuthorization[action.payload.id] = cloneDeep(newStateAuthorizationHeaders);
      }
    },
    deleteSaveState: (state: SessionCacheI, action: PayloadAction<string>) => {
      delete state.saveStates[action.payload];
      delete state.saveStatesAuthorization[action.payload];
      if (state.currentSaveState === action.payload) {
        if (Object.keys(state.saveStates).length > 0) state.currentSaveState = Object.keys(state.saveStates)[0];
        else state.currentSaveState = undefined;
      }
    },
    testedSaveState: (state: SessionCacheI, action: PayloadAction<string>) => {
      state.testedSaveState = { ...state.testedSaveState, [action.payload]: DatabaseStatus.tested };
    },
    setStateAuthorization: (state: SessionCacheI, action: PayloadAction<{ id: string; authorization: SaveStateAuthorizationHeaders }>) => {
      state.saveStatesAuthorization[action.payload.id] = action.payload.authorization;
    },
    deleteAuthorization: (state: SessionCacheI, action: PayloadAction<string>) => {
      delete state.saveStatesAuthorization[action.payload];
    },
  },
});

export const {
  selectSaveState,
  deleteSaveState,
  updateSaveStateList,
  setSaveStateList,
  addSaveState,
  testedSaveState,
  setFetchingSaveStates,
  updateSelectedSaveState,
  setStateAuthorization,
  deleteAuthorization,
} = sessionSlice.actions;

// Other code such as selectors can use the imported `RootState` type
export const sessionCacheState = (state: RootState) => state.sessionCache;
export const activeSaveState = (state: RootState): SaveState => state.sessionCache.saveStates?.[state.sessionCache.currentSaveState!];
export const activeSaveStateAuthorization = (state: RootState): SaveStateAuthorizationHeaders =>
  state.sessionCache.saveStatesAuthorization?.[state.sessionCache.currentSaveState!] || newStateAuthorizationHeaders;

export default sessionSlice.reducer;