import { State, Action, StateContext, Store } from '@ngxs/store';
import {
  SetProducts,
  DeleteProductsForProcess,
  ReloadProductsData,
  SetProductIfItDoesNotExist,
  SetProductCachedResults,
} from '../actions/product.actions';
import { Breadcrumb } from '../../models/breadcrumb.model';
import { AddBreadcrumbNoDub } from '../actions/breadcrumb.actions';
import { ItemDTO, ListResponseArgsOfItemDTO } from '@swagger/cat';
import { AppState } from './app.state';
import { UserStateSyncData } from '../../models/user-state-sync.model';
import { AppUserDataSync } from '../actions/app.actions';
import { ClearCachedResults } from '../actions/collecting-shelf.action';
import { Injectable } from '@angular/core';

export class ProductStateModel {
  products: { [key: number]: { [key: number]: ItemDTO } };
  cachedProductResults: { [id: number]: { result: ListResponseArgsOfItemDTO } };
}

@State<ProductStateModel>({
  name: 'products',
  defaults: {
    products: {},
    cachedProductResults: {},
  },
})
@Injectable()
export class ProductState {
  constructor(private store: Store) {}

  @Action(SetProducts, { cancelUncompleted: true })
  setProducts(ctx: StateContext<ProductStateModel>, { payload, search, processId, setBreadcrumb }: SetProducts) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);

    const products = { ...state.products };
    const currentProcessProduct = products[currentProcessId];

    let items = {};
    if (currentProcessProduct) {
      items = { ...currentProcessProduct };
    }

    payload.forEach((item: ItemDTO) => {
      if (item) {
        items[item.id] = item;
      }
    });

    products[processId] = items;

    if (setBreadcrumb) {
      const newBread: Breadcrumb = {
        name: (search.query.length > 12 ? search.query.substring(0, 12) + '...' : search.query) + ` (${payload.length} Ergebnisse)`,
        path: '/product/results',
      };

      this.store.dispatch(new AddBreadcrumbNoDub(newBread, 'product'));
    }
    ctx.patchState({ products });
    this.syncApiState(products, state.cachedProductResults);
  }

  @Action(SetProductIfItDoesNotExist)
  setProductIfItDoesNotExist(ctx: StateContext<ProductStateModel>, { payload }: SetProductIfItDoesNotExist) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);

    const products = { ...state.products };
    const currentProcessProduct = products[currentProcessId];

    let items = {};
    if (currentProcessProduct) {
      items = { ...currentProcessProduct };
    }

    const productExists = items[payload.id];
    if (!productExists) {
      items = { ...items, [payload.id]: payload };
    }

    products[currentProcessId] = items;

    ctx.patchState({ products });
    this.syncApiState(products, state.cachedProductResults);
  }

  @Action(DeleteProductsForProcess)
  deleteProductsForProcess(ctx: StateContext<ProductStateModel>, { payload }: DeleteProductsForProcess) {
    const state = ctx.getState();
    const products = { ...state.products };
    delete products[payload];

    ctx.patchState({ products });
    this.syncApiState(products, state.cachedProductResults);
  }

  @Action(SetProductCachedResults)
  setProductCachedResults(ctx: StateContext<ProductStateModel>, { cachedResults }: SetProductCachedResults) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);

    if (currentProcessId) {
      const cachedProductResults = { ...state.cachedProductResults };
      cachedProductResults[currentProcessId] = { result: cachedResults };

      ctx.patchState({ cachedProductResults });
      this.syncApiState(state.products, cachedProductResults);
    }
  }

  @Action(ClearCachedResults)
  clearProductCachedResults(ctx: StateContext<ProductStateModel>) {
    const state = ctx.getState();
    const currentProcessId = this.store.selectSnapshot(AppState.getCurrentProcessId);

    if (currentProcessId) {
      const cachedProductResults = { ...state.cachedProductResults };
      cachedProductResults[currentProcessId] = undefined;

      ctx.patchState({ cachedProductResults });
      this.syncApiState(state.products, cachedProductResults);
    }
  }

  @Action(ReloadProductsData)
  reloadProducts(ctx: StateContext<ProductStateModel>, { products, cachedProductResults }: ReloadProductsData) {
    ctx.setState({ products: { ...products }, cachedProductResults: cachedProductResults });
  }

  private syncApiState(
    products: { [key: number]: { [key: number]: ItemDTO } },
    cachedProductResults: { [id: number]: { result: ListResponseArgsOfItemDTO } }
  ) {
    const userSyncData: UserStateSyncData = {
      products: products,
      cachedProductResults: cachedProductResults,
    };
    const userData = JSON.stringify(userSyncData);
    this.store.dispatch(new AppUserDataSync(userData));
  }
}
