import {
  EntityId,
  PayloadAction,
  createAsyncThunk,
  createSlice,
} from '@reduxjs/toolkit';
import { RootState } from './rootReducer';
import { TreeNode } from './types';
import { fetchAll, fetchSearch } from './dataSlice';
import { getSelected, saveSelected, setSelectedNode } from './localstorage';
import { nodeSelectors, setReady } from './taskNavigatorSlice';
import { resetStatus, setSearchStr } from '../share/aufgaben-shared/slices/filterSlice';

type State = null | TreeNode;
const initialState: State = null;
const slice = createSlice({
  name: 'selectedNode',
  initialState,
  reducers: {
    setNode(_state, action: PayloadAction<TreeNode>) {
      saveSelected(action.payload.uid);
      return action.payload;
    },
    resetNode() {
      saveSelected(null);
      return null;
    },
  },
});

export default slice.reducer;

function departmentNodeID(rootState: RootState, node: TreeNode) {
  if (node === null) return;
  const isDepartment = node.path.length === 0;
  if (isDepartment) {
    return node.id;
  }

  const treeDepartmentIndex = node.path[0];
  const tree = rootState.tree.treeData[treeDepartmentIndex];
  if (tree === undefined) return;
  return tree.id;
}

export const setNode = createAsyncThunk(
  'tree/select_node',
  async (treeNode: TreeNode, thunkAPI) => {
    const rootState = thunkAPI.getState() as RootState;
    const d = thunkAPI.dispatch;

    const currentNodeDepartmentID = departmentNodeID(rootState, rootState.node);
    const nextNodeDepartmentID = departmentNodeID(rootState, treeNode);

    if (currentNodeDepartmentID != nextNodeDepartmentID) {
      // flush aktuller status
      d(resetStatus());
    }

    d(slice.actions.setNode(treeNode));
    return treeNode;
  }
);

export const selectFirstNode = createAsyncThunk(
  'tree/select_first_node',
  async (_, thunkAPI) => {
    const rootState = (await thunkAPI.getState()) as RootState;
    const treeIDs = rootState.tree.ids;
    if (treeIDs.length === 0) {
      return thunkAPI.rejectWithValue('Tree not loaded');
    }

    const firstNode: TreeNode = nodeSelectors.selectById(rootState, treeIDs[0]);

    const d = thunkAPI.dispatch;
    d(slice.actions.setNode(firstNode));
  }
);

export const resetNode = createAsyncThunk(
  'tree/remove_selected_node',
  async (_arg, thunkAPI) => {
    const d = thunkAPI.dispatch;
    d(slice.actions.resetNode());
    d(fetchAll());
  }
);

export const restoreSelected = createAsyncThunk(
  'tree/restore_seleted',
  async (_arg, thunkAPI) => {
    const nodeID = getSelected();
    const state = thunkAPI.getState() as RootState;
    const node = nodeSelectors.selectById(state, nodeID);
    const d = thunkAPI.dispatch;
    const isSearch = (window as any).searchval !== undefined;

    if (isSearch) {
        d(setReady());
        const searchString: string = (window as any).searchval;
        // clear search value
        (window as any).searchval = undefined;
        d(setSearchStr(`#${searchString}`));
        d(fetchSearch());
        return;
    }

    if (node === undefined) {
      await d(fetchAll());
      d(setReady());
      return thunkAPI.rejectWithValue(
        'Selected node unavailable ID: ' + nodeID
      );
    }

    const isRestored = state.node !== null;
    if (isRestored) {
      d(setReady());
      return thunkAPI.rejectWithValue('Selected node already restored');
    }

    d(setNode(node));
    d(fetchAll());
    d(setReady());
  }
);

export const setNodeByNodeID = createAsyncThunk(
  'tree/select_node',
  async (nodeID: EntityId, thunkAPI) => {
    const state = thunkAPI.getState() as RootState;
    const node = nodeSelectors.selectById(state, nodeID);
    const d = thunkAPI.dispatch;
    d(setNode(node));
  }
);
