import { State, Action, StateContext, Store, StateOperator, Selector } from '@ngxs/store';
import { ProcessBreadcrumbs } from '../../models/process-breadcrumbs.model';
import * as actions from '../actions/breadcrumb.actions';
import { Breadcrumb } from '../../models/breadcrumb.model';
import { patch, removeItem, iif, updateItem, append } from '@ngxs/store/operators';
import { AppState } from './app.state';
import { UserStateSyncData } from '../../models/user-state-sync.model';
import { AppUserDataSync } from '../actions/app.actions';
import { ProcessBreadcrumb } from '../../models/process-breadcrumb.model';
import { ChangeCurrentRoute, SetPreviusPath } from '../actions/process.actions';
import { ModuleSwitcherService } from '../../services/module-switcher.service';
import { ModuleSwitcher } from '../../models/app-switcher.enum';
import { SharedSelectors } from '../selectors/shared.selectors';
import { AppSelectors } from '../selectors/app.selectors';
import { FILIALE_LANDING_PAGE } from '../../utils/app.constants';
import { Router } from '@angular/router';
import { Injectable } from '@angular/core';

export class BreadcrumbsStateModel {
  processesBreadcrumbs: ProcessBreadcrumb;
  activeCrumbs: { [id: number]: string };
  previusMenuPath: { [id: number]: { crumb: string; path: string }[] };
}

@State<BreadcrumbsStateModel>({
  name: 'breadcrumbs',
  defaults: {
    processesBreadcrumbs: {
      customer: [],
      product: [],
      shelf: [],
      shoppingCart: [],
      checkout: [],
      goodsin: [
        {
          processId: -1,
          breadcrumbs: [{ name: 'Abholfach', path: '/branch/main' }],
        },
      ],
      // taskCalendar: [],
      remission: [],
    },
    activeCrumbs: {},
    previusMenuPath: {},
  },
})
@Injectable()
export class BreadcrumbsState {
  @Selector()
  static hasRemissionBreadcrumbs(breadcrumbsState: BreadcrumbsStateModel) {
    return breadcrumbsState.processesBreadcrumbs.remission && breadcrumbsState.processesBreadcrumbs.remission.length > 0;
  }

  editAddaddressPageFilter = (path: string) =>
    // tslint:disable-next-line: max-line-length
    path.includes('/billing/edit/') &&
    path.includes('/delivery/edit/') &&
    path.includes('/billing/add/') &&
    // tslint:disable-next-line: semicolon
    path.includes('/delivery/add/');

  constructor(private store: Store, private router: Router, private moduleSwitcherService: ModuleSwitcherService) {}

  @Action(actions.AddBreadcrumb)
  addBreadcrumb(ctx: StateContext<BreadcrumbsStateModel>, { payload, breadcrumbsId, isMainBreadcrumb }: actions.AddBreadcrumb) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);

    const currentProcessBreadcrumbs: ProcessBreadcrumbs[] = state.processesBreadcrumbs[breadcrumbsId];
    const pathData = payload.path.split('/');

    if (!currentProcessBreadcrumbs || !currentProcessId) {
      return;
    }

    const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
    let processesBreadcrumbs: ProcessBreadcrumbs[];
    if (processBreadcrumbsFound) {
      processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
        if (pb.processId === currentProcessId) {
          return this.updateProcessBreadcrumb(payload, isMainBreadcrumb, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
        } else {
          return pb;
        }
      });
    } else {
      processesBreadcrumbs = [...currentProcessBreadcrumbs, { processId: currentProcessId, breadcrumbs: [payload] }];
    }

    const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, breadcrumbsId, processesBreadcrumbs);

    if ((pathData.length > 0 && pathData[pathData.length - 1] === 'billing') || pathData[pathData.length - 1] === 'delivery') {
      pathData.pop();
      const newPath = pathData.join('/');
      this.store.dispatch(new SetPreviusPath(newPath));
    }

    let currentProcessActiveCrumbs = {};
    if (state.activeCrumbs) {
      currentProcessActiveCrumbs = { ...state.activeCrumbs };

      if (currentProcessActiveCrumbs[currentProcessId]) {
        currentProcessActiveCrumbs[currentProcessId] = breadcrumbsId;
      } else {
        currentProcessActiveCrumbs = {
          ...currentProcessActiveCrumbs,
          [currentProcessId]: breadcrumbsId,
        };
      }
    }

    const previousMenuPath = this.updatePreviousMenuPath(currentProcessId, state.previusMenuPath, state.activeCrumbs, payload);

    ctx.setState({
      processesBreadcrumbs: {
        customer: updatedCrumbs['customer'],
        product: updatedCrumbs['product'],
        shelf: updatedCrumbs['shelf'],
        shoppingCart: updatedCrumbs['shoppingCart'],
        checkout: updatedCrumbs['checkout'],
        goodsin: updatedCrumbs['goodsin'],
        remission: updatedCrumbs['remission'],
        // taskCalendar: updatedCrumbs['tasksCalendar']
      },
      activeCrumbs: currentProcessActiveCrumbs,
      previusMenuPath: previousMenuPath,
    });
    this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, previousMenuPath);
  }

  @Action(actions.AddBreadcrumbNoDub)
  addBreadcrumbNoDub(ctx: StateContext<BreadcrumbsStateModel>, { payload, breadcrumbsId }: actions.AddBreadcrumbNoDub) {
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    const state = ctx.getState();
    const currentProcessBreadcrumbs: ProcessBreadcrumbs[] = state.processesBreadcrumbs[breadcrumbsId];
    const pathData = payload.path.split('/');

    if (!currentProcessBreadcrumbs) {
      return;
    }

    const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
    let processesBreadcrumbs: ProcessBreadcrumbs[];
    if (processBreadcrumbsFound) {
      const currentBreadcrumb = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId).breadcrumbs;
      if (currentBreadcrumb) {
        const lastPath = currentBreadcrumb[currentBreadcrumb.length - 1];
        if (lastPath && lastPath.path !== payload.path) {
          return;
        }
      }
      processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
        if (pb.processId === currentProcessId) {
          return this.updateProcessBreadcrumbFoNoDub(payload, false, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
        } else {
          return pb;
        }
      });

      const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, breadcrumbsId, processesBreadcrumbs);

      ctx.patchState({
        processesBreadcrumbs: {
          customer: updatedCrumbs['customer'],
          product: updatedCrumbs['product'],
          shelf: updatedCrumbs['shelf'],
          shoppingCart: updatedCrumbs['shoppingCart'],
          checkout: updatedCrumbs['checkout'],
          goodsin: updatedCrumbs['goodsin'],
          remission: updatedCrumbs['remission'],
          // taskCalendar: updatedCrumbs['tasksCalendar']
        },
      });
      this.syncApiState(ctx.getState().processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
    }
  }

  @Action(actions.DeleteLastPreviousPath)
  deleteLastPreviousPath(ctx: StateContext<BreadcrumbsStateModel>) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    if (!state || !currentProcessId) {
      return;
    }

    const previousMenuPath = { ...state.previusMenuPath };
    if (previousMenuPath && previousMenuPath[currentProcessId]) {
      const updatedPrevousPaths = {
        ...previousMenuPath,
        [currentProcessId]: previousMenuPath[currentProcessId].slice(0, previousMenuPath[currentProcessId].length - 1),
      };
      ctx.patchState({ previusMenuPath: updatedPrevousPaths });
    }
  }

  @Action(actions.ResetBreadcrumbsTo)
  resetBreadCrumbs(
    ctx: StateContext<BreadcrumbsStateModel>,
    { payload, breadcrumbsId, isMainBreadcrumb, isBranchView }: actions.ResetBreadcrumbsTo
  ) {
    const currentProcessId = isBranchView ? -1 : this.store.selectSnapshot(AppState.getCurrentProcessId);
    const state = ctx.getState();

    const currentProcessBreadcrumbs = state.processesBreadcrumbs[breadcrumbsId];

    const pathData = payload.path.split('/');
    if (!currentProcessBreadcrumbs) {
      return;
    }

    const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
    let processesBreadcrumbs: ProcessBreadcrumbs[];
    if (processBreadcrumbsFound) {
      processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
        if (pb.processId === currentProcessId) {
          return this.updateProcessBreadcrumb(payload, isMainBreadcrumb, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
        } else {
          return pb;
        }
      });
    } else {
      processesBreadcrumbs = [...currentProcessBreadcrumbs, { processId: currentProcessId, breadcrumbs: [payload] }];
    }

    const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, breadcrumbsId, processesBreadcrumbs);

    if (updatedCrumbs[breadcrumbsId].length === 0) {
      updatedCrumbs[breadcrumbsId] = [{ processId: currentProcessId, breadcrumbs: [payload] }];
    }

    let currentProcessActiveCrumbs = {};
    if (state.activeCrumbs) {
      currentProcessActiveCrumbs = { ...state.activeCrumbs };

      if (currentProcessActiveCrumbs[currentProcessId]) {
        currentProcessActiveCrumbs[currentProcessId] = breadcrumbsId;
      } else {
        currentProcessActiveCrumbs = {
          ...currentProcessActiveCrumbs,
          [currentProcessId]: breadcrumbsId,
        };
      }
    }

    const previousMenuPath = this.updatePreviousMenuPath(currentProcessId, state.previusMenuPath, state.activeCrumbs, payload);

    ctx.setState({
      processesBreadcrumbs: {
        customer: updatedCrumbs['customer'],
        product: updatedCrumbs['product'],
        shelf: updatedCrumbs['shelf'],
        shoppingCart: updatedCrumbs['shoppingCart'],
        checkout: updatedCrumbs['checkout'],
        goodsin: updatedCrumbs['goodsin'],
        remission: updatedCrumbs['remission'],
        // taskCalendar: updatedCrumbs['taskCalendar']
      },
      activeCrumbs: currentProcessActiveCrumbs,
      previusMenuPath: previousMenuPath,
    });

    this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, previousMenuPath);
  }

  private updatePreviousMenuPath(
    currentProcessId: number,
    menuPath: { [id: number]: { crumb: string; path: string }[] },
    crumbs: { [id: number]: string },
    breadcrumb: Breadcrumb
  ) {
    let previousMenuPath: {
      [id: number]: { crumb: string; path: string }[];
    } = {};
    const activeModule = this.store.selectSnapshot(AppState.activeModule);
    const defaultCrumb = activeModule === ModuleSwitcher.Customer ? 'dashboard' : 'goodsin';
    const defaultPath = activeModule === ModuleSwitcher.Customer ? '/dashboard' : FILIALE_LANDING_PAGE;
    if (currentProcessId) {
      const route = this.router.url;
      const currentProcessCrumb = crumbs[currentProcessId];
      const isCustomeOrderDetails = /^[/]customer[/][0-9]{1,}[/]order[/][0-9]{1,}$/;
      if (route) {
        if (
          (route === '/cart/confirmation' && breadcrumb && !isCustomeOrderDetails.test(breadcrumb.path)) ||
          (menuPath &&
            menuPath[currentProcessId] &&
            menuPath[currentProcessId][menuPath[currentProcessId].length - 1] &&
            menuPath[currentProcessId][menuPath[currentProcessId].length - 1].path === '/cart/confirmation')
        ) {
          previousMenuPath = {
            ...menuPath,
            [currentProcessId]: [{ crumb: defaultCrumb, path: defaultPath }],
          };
        } else {
          if (menuPath[currentProcessId]) {
            const lastPath = menuPath[currentProcessId][menuPath[currentProcessId].length - 1];
            const isOntheSamePage = lastPath ? lastPath.crumb === currentProcessCrumb && lastPath.path === route : false;
            if (!isOntheSamePage) {
              previousMenuPath = {
                ...menuPath,
                [currentProcessId]: [
                  ...menuPath[currentProcessId],
                  {
                    crumb: currentProcessCrumb ? currentProcessCrumb : defaultCrumb,
                    path: currentProcessCrumb ? route : defaultPath,
                  },
                ],
              };
            } else {
              return menuPath;
            }
          } else {
            previousMenuPath = {
              ...menuPath,
              [currentProcessId]: [
                {
                  crumb: currentProcessCrumb ? currentProcessCrumb : defaultCrumb,
                  path: defaultPath,
                },
              ],
            };
          }
        }
      }
    } else {
      return menuPath;
    }
    return previousMenuPath;
  }

  @Action(actions.ResetBreadcrumbsAfterOrder)
  resetBreadcrumbsAfterOrder(ctx: StateContext<BreadcrumbsStateModel>) {
    // TODO: implement reset breadcrumbs after ordering
  }

  updateProcessBreadcrumb(
    payload: Breadcrumb,
    isMainBreadcrumb: boolean = false,
    pathEnding: string
  ): StateOperator<Readonly<ProcessBreadcrumbs>> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      if (isMainBreadcrumb) {
        return <ProcessBreadcrumbs>{
          ...processBreadcrumbs,
          breadcrumbs: [payload],
        };
      } else {
        const updatedBreadcrumbs = updateItem<Breadcrumb>(
          (breadcrumb) =>
            breadcrumb.name === payload.name ||
            (breadcrumb.path.substring(0, 16) === payload.path.substring(0, 16) &&
              pathEnding !== 'billing' &&
              pathEnding !== 'delivery' &&
              this.editAddaddressPageFilter(payload.path)),
          {
            name: payload.name,
            path: payload.path,
            queryParams: payload.queryParams,
          }
        )(processBreadcrumbs.breadcrumbs);

        let breadcrumbs = iif<Breadcrumb[]>(
          (item) => item.find((b) => b.name === payload.name) === undefined,
          (item: Breadcrumb[]) => append<Breadcrumb>([payload])(item)
        )(updatedBreadcrumbs);
        const isCustomeOrderDetails = /^[/]customer[/][0-9]{1,}[/]order[/][0-9]{1,}$/;
        if (breadcrumbs && breadcrumbs[0].path === 'cart/confirmation' && !isCustomeOrderDetails.test(payload.path)) {
          breadcrumbs = [breadcrumbs[0]];
        }

        return <ProcessBreadcrumbs>{
          ...processBreadcrumbs,
          breadcrumbs: breadcrumbs,
        };
      }
    };
  }

  updateProcessBreadcrumbFoNoDub(
    payload: Breadcrumb,
    isMainBreadcrumb: boolean = false,
    pathEnding: string
  ): StateOperator<Readonly<ProcessBreadcrumbs>> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      if (isMainBreadcrumb) {
        return <ProcessBreadcrumbs>{
          ...processBreadcrumbs,
          breadcrumbs: [payload],
        };
      } else {
        const updatedBreadcrumbs = updateItem<Breadcrumb>((breadcrumb) => breadcrumb.path === payload.path, {
          name: payload.name,
          path: payload.path,
          queryParams: payload.queryParams,
        })(processBreadcrumbs.breadcrumbs);

        return <ProcessBreadcrumbs>{
          ...processBreadcrumbs,
          breadcrumbs: updatedBreadcrumbs,
        };
      }
    };
  }

  updateNameAndQueryParams(
    payload: Breadcrumb,
    isMainBreadcrumb: boolean = false,
    pathEnding: string
  ): StateOperator<Readonly<ProcessBreadcrumbs>> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      if (isMainBreadcrumb) {
        return <ProcessBreadcrumbs>{
          ...processBreadcrumbs,
          breadcrumbs: [payload],
        };
      } else {
        const updatedBreadcrumbs = updateItem<Breadcrumb>(
          (breadcrumb) =>
            breadcrumb.name === payload.name ||
            ((breadcrumb.path.substring(0, 16) === payload.path.substring(0, 16) ||
              breadcrumb.path.substring(0, 14) === payload.path.substring(0, 14)) &&
              pathEnding !== 'billing' &&
              pathEnding !== 'delivery' &&
              !this.editAddaddressPageFilter(payload.path)),
          {
            name: payload.name,
            path: payload.path,
            queryParams: payload.queryParams,
          }
        )(processBreadcrumbs.breadcrumbs);

        return <ProcessBreadcrumbs>{
          ...processBreadcrumbs,
          breadcrumbs: updatedBreadcrumbs,
        };
      }
    };
  }

  @Action(actions.PopBreadcrumbsAfterCurrent)
  popBreadcrumbs(ctx: StateContext<BreadcrumbsStateModel>, { path }: actions.PopBreadcrumbsAfterCurrent) {
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    const state = ctx.getState();
    const previusMenuPath = { ...state.previusMenuPath };

    if (previusMenuPath && currentProcessId) {
      const currentPreviousMenuPaths = previusMenuPath[currentProcessId];
      if (currentPreviousMenuPaths) {
        const pathIndex = currentPreviousMenuPaths.findIndex((p) => p && p.path === path);
        if (pathIndex !== -1) {
          const updatedMenuPaths = [...currentPreviousMenuPaths.slice(0, pathIndex)];
          previusMenuPath[currentProcessId] = updatedMenuPaths;
          ctx.patchState({ previusMenuPath });
          this.syncApiState(state.processesBreadcrumbs, state.activeCrumbs, previusMenuPath);
        }
      }
    }
  }

  deleteBreadcrumbsByIndex(index: number): StateOperator<ProcessBreadcrumbs> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      const breadcrumbs = removeItem<Breadcrumb>((b) => processBreadcrumbs.breadcrumbs.indexOf(b) > index)(processBreadcrumbs.breadcrumbs);
      return <ProcessBreadcrumbs>{
        ...processBreadcrumbs,
        breadcrumbs: breadcrumbs,
      };
    };
  }

  @Action(actions.PopBreadcrumbsBeforeCurrent)
  popBreadcrumbsBefore(ctx: StateContext<BreadcrumbsStateModel>, { breadcrumbsId }: actions.PopBreadcrumbsBeforeCurrent) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    const currentProcessBreadcrumbs = state.processesBreadcrumbs[breadcrumbsId];

    let currentProcessActiveCrumbs = {};
    if (state.activeCrumbs) {
      currentProcessActiveCrumbs = { ...state.activeCrumbs };

      if (currentProcessActiveCrumbs[currentProcessId]) {
        currentProcessActiveCrumbs[currentProcessId] = breadcrumbsId;
      } else {
        currentProcessActiveCrumbs = {
          ...currentProcessActiveCrumbs,
          [currentProcessId]: breadcrumbsId,
        };
      }
    }

    ctx.setState({
      processesBreadcrumbs: {
        customer: [],
        product: [],
        shelf: [],
        shoppingCart: [],
        checkout: currentProcessBreadcrumbs,
        goodsin: [],
        remission: [],
        // taskCalendar: []
      },
      activeCrumbs: currentProcessActiveCrumbs,
      previusMenuPath: {},
    });

    this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, state.previusMenuPath);
  }

  leaveOnlyLastBreadcrumb(): StateOperator<ProcessBreadcrumbs> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      const breadcrumbs = [processBreadcrumbs.breadcrumbs[processBreadcrumbs.breadcrumbs.length - 1]];
      return <ProcessBreadcrumbs>{
        ...processBreadcrumbs,
        breadcrumbs: breadcrumbs,
      };
    };
  }

  clearBreadcrumbsForProcess(): StateOperator<ProcessBreadcrumbs> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      return <ProcessBreadcrumbs>{
        ...processBreadcrumbs,
        breadcrumbs: [],
      };
    };
  }

  @Action(actions.PopLastBreadcrumbs)
  popLastBreadcrumb(ctx: StateContext<BreadcrumbsStateModel>, { path }: actions.PopLastBreadcrumbs) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    const activeCrumbs = state.activeCrumbs[currentProcessId];
    if (!activeCrumbs) {
      return;
    }
    if (
      state.previusMenuPath[currentProcessId] &&
      state.previusMenuPath[currentProcessId].length > 0 &&
      state.previusMenuPath[currentProcessId][state.previusMenuPath[currentProcessId].length - 1].crumb !== activeCrumbs
    ) {
      return;
    }
    const currentProcessBreadcrumbs = state.processesBreadcrumbs[activeCrumbs];

    const processesBreadcrumbs = updateItem<ProcessBreadcrumbs>(
      (item) => item.processId === currentProcessId,
      (item) => this.deleteLastBreadcrumbForProcess(path, currentProcessId, state.previusMenuPath)(item)
    )(currentProcessBreadcrumbs);

    const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, activeCrumbs, processesBreadcrumbs);

    ctx.setState({
      processesBreadcrumbs: {
        customer: updatedCrumbs['customer'],
        product: updatedCrumbs['product'],
        shelf: updatedCrumbs['shelf'],
        shoppingCart: updatedCrumbs['shoppingCart'],
        checkout: updatedCrumbs['checkout'],
        goodsin: updatedCrumbs['goodsin'],
        remission: updatedCrumbs['remission'],
        // taskCalendar: updatedCrumbs['tasksCalendar']
      },
      activeCrumbs: state.activeCrumbs,
      previusMenuPath: state.previusMenuPath,
    });
    this.syncApiState(ctx.getState().processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
  }

  deleteLastBreadcrumbForProcess(
    path: string,
    processId: number,
    previusMenuPath: { [id: number]: { crumb: string; path: string }[] }
  ): StateOperator<ProcessBreadcrumbs> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      const breadcrumbs = [...processBreadcrumbs.breadcrumbs];
      const index = breadcrumbs.findIndex((t) => t.path === path);

      let updatedBreads = [...breadcrumbs];
      if (index !== -1) {
        updatedBreads = breadcrumbs.slice(0, index + 1);
      } else {
        updatedBreads.pop();
        if (
          previusMenuPath[processId] &&
          previusMenuPath[processId].length &&
          previusMenuPath[processId][previusMenuPath[processId].length - 1]
        ) {
          this.store.dispatch(new ChangeCurrentRoute(previusMenuPath[processId][previusMenuPath[processId].length - 1].path));
        }
      }

      return <ProcessBreadcrumbs>{
        ...processBreadcrumbs,
        breadcrumbs: updatedBreads,
      };
    };
  }

  @Action(actions.UpdateBreadcrumbQueryParams)
  updateBreadcrumbQueryParams(
    ctx: StateContext<BreadcrumbsStateModel>,
    { path, breadcrumbId, queryParams }: actions.UpdateBreadcrumbQueryParams
  ) {
    const state = ctx.getState();
    if (!state) {
      return;
    }
    const currentProcessBreadcrumbs = state.processesBreadcrumbs;
    if (!currentProcessBreadcrumbs) {
      return;
    }
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    const breadcrumbs: ProcessBreadcrumbs[] = currentProcessBreadcrumbs[breadcrumbId];
    if (!currentProcessId || !breadcrumbs) {
      return;
    }

    const processesBreadcrumbs = {
      ...currentProcessBreadcrumbs,
      [breadcrumbId]: [
        ...currentProcessBreadcrumbs[breadcrumbId],
        breadcrumbs.map((t) => {
          if (t && t.processId === currentProcessId) {
            return {
              processId: currentProcessId,
              breadcrumbs: t.breadcrumbs.map((b) => {
                if (b && b.path === path) {
                  return {
                    name: b.name,
                    path: b.path,
                    queryParams: queryParams,
                  };
                }
                return b;
              }),
            };
          }
          return t;
        }),
      ],
    };
    ctx.patchState({
      processesBreadcrumbs,
    });
    this.syncApiState(processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
  }

  @Action(actions.UpdateCurrentBreadcrumbName)
  updateCurrentBreadcrumbName(
    ctx: StateContext<BreadcrumbsStateModel>,
    { payload, queryParams, breadcrumbsId }: actions.UpdateCurrentBreadcrumbName
  ) {
    const state = ctx.getState();
    if (!state) {
      return;
    }
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);
    if (!currentProcessId) {
      return;
    }
    const activeCrumbs = state.activeCrumbs[currentProcessId];
    if (!activeCrumbs) {
      return;
    }
    const currentProcessBreadcrumbs = state.processesBreadcrumbs[activeCrumbs];
    const pathData = payload.split('/');

    const processBreadcrumbsFound = currentProcessBreadcrumbs.find((t) => t.processId === currentProcessId) !== undefined;
    let processesBreadcrumbs: ProcessBreadcrumbs[];
    if (processBreadcrumbsFound) {
      processesBreadcrumbs = currentProcessBreadcrumbs.map((pb: ProcessBreadcrumbs) => {
        if (pb.processId === currentProcessId && pb.breadcrumbs && pb.breadcrumbs.length > 0) {
          const newBreadcrumb = {
            ...pb.breadcrumbs[pb.breadcrumbs.length - 1],
            name: payload,
            queryParams: queryParams,
          };
          return this.updateNameAndQueryParams(newBreadcrumb, false, pathData.length > 0 ? pathData[pathData.length - 1] : '')(pb);
        } else {
          return pb;
        }
      });
    }

    const updatedCrumbs = this.udpatedBreadcrumbsObject(state.processesBreadcrumbs, activeCrumbs, processesBreadcrumbs);

    ctx.setState({
      processesBreadcrumbs: {
        customer: updatedCrumbs['customer'],
        product: updatedCrumbs['product'],
        shelf: updatedCrumbs['shelf'],
        shoppingCart: updatedCrumbs['shoppingCart'],
        checkout: updatedCrumbs['checkout'],
        goodsin: updatedCrumbs['goodsin'],
        remission: updatedCrumbs['remission'],
        // taskCalendar: updatedCrumbs['tasksCalendar']
      },
      activeCrumbs: state.activeCrumbs,
      previusMenuPath: state.previusMenuPath,
    });
    this.syncApiState(ctx.getState().processesBreadcrumbs, state.activeCrumbs, state.previusMenuPath);
  }

  updateBreadcrumbsForProcess(payload: string, currentPath: string, queryParams: any): StateOperator<Readonly<ProcessBreadcrumbs>> {
    return (processBreadcrumbs: Readonly<ProcessBreadcrumbs>) => {
      const breadcrumbs = updateItem<Breadcrumb>(
        (item) => item.path === currentPath,
        (item) =>
          <Breadcrumb>{
            ...item,
            name: payload,
            queryParams: queryParams,
          }
      )(processBreadcrumbs.breadcrumbs);
      return patch<ProcessBreadcrumbs>({
        breadcrumbs,
      })(processBreadcrumbs);
    };
  }

  @Action(actions.DeleteBreadcrumbsForProcess)
  deleteBreadcrumbsForProcess(ctx: StateContext<BreadcrumbsStateModel>, { payload }: actions.DeleteBreadcrumbsForProcess) {
    const state = ctx.getState();

    if (state.processesBreadcrumbs) {
      const customer = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'customer', payload);
      const product = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'product', payload);
      const shelf = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'shelf', payload);
      const shoppingCart = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'shoppingCart', payload);
      const checkout = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'checkout', payload);
      const goodsin = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'goodsin', payload);
      const remission = this.deleteBreadsForMenu(state.processesBreadcrumbs, 'remission', payload);
      /*       const taskCalendar = this.deleteBreadsForMenu(
        state.processesBreadcrumbs,
        'taskCalendar',
        payload
      ); */

      const currentActiveCrumbs = { ...state.activeCrumbs };
      if (currentActiveCrumbs[payload]) {
        delete currentActiveCrumbs[payload];
      }

      const activeCrumbs = { ...state.activeCrumbs };
      delete activeCrumbs[payload];

      const previousMenuPath = { ...state.previusMenuPath };
      delete previousMenuPath[payload];

      ctx.setState({
        processesBreadcrumbs: {
          customer,
          product,
          shelf,
          shoppingCart,
          checkout,
          goodsin,
          remission,
          // taskCalendar
        },
        activeCrumbs: activeCrumbs,
        previusMenuPath: previousMenuPath,
      });
      this.syncApiState(ctx.getState().processesBreadcrumbs, activeCrumbs, previousMenuPath);
    }
  }

  private deleteBreadsForMenu(breds: ProcessBreadcrumb, menu: string, processId: number) {
    const menuBreadcrumbs = breds[menu];
    const processesBreadcrumbs = removeItem<ProcessBreadcrumbs>((item) => item.processId === processId)(menuBreadcrumbs);
    return processesBreadcrumbs;
  }

  @Action(actions.UpdateActiveCrumb)
  updateActiveCrumb(ctx: StateContext<BreadcrumbsStateModel>, { crumb }: actions.UpdateActiveCrumb) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);

    if (state && currentProcessId) {
      let currentProcessActiveCrumbs = {};
      if (state.activeCrumbs) {
        currentProcessActiveCrumbs = { ...state.activeCrumbs };

        if (currentProcessActiveCrumbs[currentProcessId]) {
          currentProcessActiveCrumbs[currentProcessId] = crumb;
        } else {
          currentProcessActiveCrumbs = {
            ...currentProcessActiveCrumbs,
            [currentProcessId]: crumb,
          };
        }
      }

      ctx.patchState({ activeCrumbs: currentProcessActiveCrumbs });
      this.syncApiState(ctx.getState().processesBreadcrumbs, currentProcessActiveCrumbs, state.previusMenuPath);
    }
  }

  @Action(actions.ReloadBreadcrumbsData)
  reloadBreadcrumbs(
    ctx: StateContext<BreadcrumbsStateModel>,
    { processesBreadcrumbs, activeCrumbs, previusMenuPath }: actions.ReloadBreadcrumbsData
  ) {
    const crumbs = {
      processesBreadcrumbs: {
        customer: processesBreadcrumbs['customer'],
        product: processesBreadcrumbs['product'],
        shelf: processesBreadcrumbs['shelf'],
        shoppingCart: processesBreadcrumbs['shoppingCart'],
        checkout: processesBreadcrumbs['checkout'],
        goodsin: processesBreadcrumbs['goodsin'],
        remission: processesBreadcrumbs['remission'],
        // taskCalendar: processesBreadcrumbs['taskCalendar'],
      },
      activeCrumbs: activeCrumbs,
      previusMenuPath: previusMenuPath,
    };

    ctx.setState({ ...crumbs });
    this.syncApiState(ctx.getState().processesBreadcrumbs, activeCrumbs, previusMenuPath);
  }

  private syncApiState(
    processesBreadcrumbs: ProcessBreadcrumb,
    activeCrumbs: { [id: number]: string },
    previusMenuPath: { [id: number]: { crumb: string; path: string }[] }
  ) {
    const userSyncData: UserStateSyncData = {
      processesBreadcrumbs,
      activeCrumbs,
      previusMenuPath,
    };

    const userData = JSON.stringify(userSyncData);
    this.store.dispatch(new AppUserDataSync(userData));
  }

  private udpatedBreadcrumbsObject(data: ProcessBreadcrumb, breadcrumbsId: string, newCrumbs: ProcessBreadcrumbs[]) {
    const obj = {};
    Object.keys(data).forEach((key) => {
      if (key === breadcrumbsId) {
        obj[key] = newCrumbs;
      } else {
        obj[key] = data[key];
      }
    });

    return obj;
  }
}
