// resources\assets\ts\DataPage\taskNavigatorSlice.ts
import {
  EntityState,
  PayloadAction,
  createAsyncThunk,
  createEntityAdapter,
  createSlice,
} from '@reduxjs/toolkit';
import { RootState } from './rootReducer';
import { TreeNode } from './types';
import { clearTable } from './tableSlice';
import { fetchAll } from './dataSlice';
import { getExpanded, getSelected, saveExpanded } from './localstorage';
import { restoreSelected, setNode } from './nodeSlice';
import axios from 'B/axios';

const nodesAdapter = createEntityAdapter<TreeNode>({
  selectId: (n) => n.uid,
});

function toNode(treeData): TreeNode {
  return {
    id: treeData.id,
    uid: treeData.uid,
    nodeID: `${treeData.type}::${treeData.id}`,
    name: treeData.name,
    isExpandable: treeData.expandable === 1,
    isCreateable: treeData.createable > 0,
    type: treeData.type,
    path: [],
    selected: false,
  };
}

const fetchGroup = createAsyncThunk(
  'tree/fetch_group',
  async (node: TreeNode) => {
    const departmentID = node.id;
    const r = await axios.get(`/tree/${departmentID}`);
    return { parent: node, child: r.data.map(toNode) };
  },
);

const fetchGrp = createAsyncThunk('tree/fetch_grp', async (node: TreeNode) => {
  const groupID = node.id;
  const r = await axios.get(`/tree/groups/${groupID}`);
  return { parent: node, child: r.data.map(toNode) };
});

export const fetchTree = createAsyncThunk('tree/fetch_tree', async () => {
  const r = await axios.get('/tree');
  const nodes: TreeNode[] = r.data.map(toNode);

  return nodes;
});

function updateTreeReducer(
  state: State,
  { payload }: PayloadAction<{ parent: TreeNode; child: TreeNode[] }>,
) {
  const { parent, child: frozenChild } = payload;
  const child = JSON.parse(JSON.stringify(frozenChild));

  nodesAdapter.upsertMany(state, child);

  let pathIndex = [...parent.path];
  let nodeToAttach;
  let treeNodes = state.treeData;
  for (let i of parent.path) {
    treeNodes = treeNodes[i].childrens;
  }

  for (let i = 0; i < treeNodes.length; i++) {
    const parentNode: TreeNode = treeNodes[i];
    const isParent = parentNode.uid === parent.uid;
    if (isParent === false) continue;

    nodeToAttach = parentNode;
    pathIndex.push(i);
  }

  for (let cn of child) {
    cn.path = pathIndex;
  }

  nodeToAttach.childrens = child;
  state.nodeIdsWithChildren.push(parent.nodeID);
  state.expanded.push(parent.nodeID);
  saveExpanded(state.expanded);
}

type status = 'init' | 'restore' | 'ready';
type State = EntityState<TreeNode> & {
  status: status;
  restore: string[];
  treeData: TreeNode[];
  expanded: string[];
  nodeIdsWithChildren: string[];
};
const initialState: State = nodesAdapter.getInitialState({
  status: 'init',
  restore: getExpanded(),
  treeData: [],
  expanded: [],
  nodeIdsWithChildren: [],
});

const slice = createSlice({
  name: 'monitor/tree',
  initialState,
  reducers: {
    close(state, { payload }: PayloadAction<number>) {
      state.expanded.splice(payload, 1);
      saveExpanded(state.expanded);
    },
    open(state, { payload }: PayloadAction<string>) {
      state.expanded.push(payload);
      saveExpanded(state.expanded);
    },
    setReady(state) {
      state.status = 'ready';
    },
  },
  extraReducers: (builder) => {
    builder
      .addCase(fetchTree.fulfilled, (state, { payload }) => {
        nodesAdapter.setAll(state, payload);
        state.treeData = payload;
        state.status = 'restore';
      })
      .addCase(fetchGroup.fulfilled, updateTreeReducer)
      .addCase(fetchGrp.fulfilled, updateTreeReducer);
  },
});

export default slice.reducer;
export const { close, open, setReady } = slice.actions;

export const toggleNode = createAsyncThunk(
  'tree/toggle_node',
  async (node: TreeNode, thunkAPI) => {
    const state = (thunkAPI.getState() as RootState).tree;

    const nodeIndex = state.expanded.indexOf(node.nodeID);
    const isOpen = nodeIndex > -1;
    if (isOpen) {
      return thunkAPI.dispatch(close(nodeIndex));
    }

    const isExpand = state.nodeIdsWithChildren.indexOf(node.nodeID) > -1;
    if (isExpand) {
      return thunkAPI.dispatch(open(node.nodeID));
    }

    // const type = nodeID
    // fetch node and expand
    switch (node.type) {
      case 'department':
        return thunkAPI.dispatch(fetchGroup(node));
      case 'GRP':
        return thunkAPI.dispatch(fetchGrp(node));
      default:
        return;
    }
  },
);

const toggleNodeRecursively = async (segments, index, thunkAPI) => {
  if (index < 0) return; // Base case: stop recursion when all segments are processed

  //segment is like the following ['grp2110', 'GRP2078', 'BER13'] and get selected returns GRP::2110 check if the first element of the segment is the selected element return
  //console.log(getSelected(), segments[0].match(/\d+$/)[0]);
  if (getSelected() === `GRP::${segments[0].match(/\d+$/)[0]}`) return;
  // Extract the node ID from the segment based on expected format like "/grp31159"
  const nodeId = Number(segments[index].match(/\d+$/)[0]); // Extracts numeric part
  if (!nodeId) return; // Stop if no ID found

  const state = thunkAPI.getState();
  const nodes = state.tree.entities; // Construct node key and get node
  const d = thunkAPI.dispatch;
  let node;
  for (let n in nodes) {
    if (nodes[n].id === nodeId) {
      node = nodes[n];
      break;
    }
  }
  if (!node) {
    console.error(`Node not found: GRP${nodeId}`);
    return; // Node not found, stop processing
  }

  // Check if the node is already expanded
  if (!state.tree.expanded.includes(node.nodeID)) {
    // Assume toggleExpandNode is an action to toggle the node
    await d(toggleNode(node));
  }

  // Recursive call with the next segment index
  toggleNodeRecursively(segments, index - 1, thunkAPI);
  if (index === 0) {
    await d(clearTable());
    await d(setNode(node));
    d(fetchAll());
  }
};

export const toggleNodesByPath = createAsyncThunk(
  'tree/toggleNodesByPath',
  async (path: string, thunkAPI) => {
    const segments = path.split('/').filter(Boolean); // Split the path and clean it from empty segments
    toggleNodeRecursively(segments, segments.length - 1, thunkAPI); // Start recursion from the last segment
  },
);

export const nodeSelectors = nodesAdapter.getSelectors(
  (s: RootState) => s.tree,
);

export const restoreTree = createAsyncThunk(
  'tree/restore_open',
  async (index: number, thunkAPI) => {
    const state = thunkAPI.getState() as RootState;
    const expandedIds = state.tree.restore;
    const d = thunkAPI.dispatch;
    const id = expandedIds[index];
    if (id === undefined) {
      d(setReady());
      return d(restoreSelected());
    }

    const node = nodeSelectors.selectById(state, id);
    if (node === undefined) {
      saveExpanded([]);
      return d(restoreSelected());
    }

    await d(toggleNode(node));
    await d(restoreTree(index + 1));
  },
);
