import { Injectable } from '@angular/core';
import { Remission } from '../../models/remission.model';
import { RemissionExistingOverview } from '../../models/remission-existing-overview.model';
import { State, Action, StateContext, Store } from '@ngxs/store';
import * as actions from '../actions/remission.actions';
import { UserStateSyncData } from '../../models/user-state-sync.model';
import { AppUserDataSync } from '../actions/app.actions';
import { isNullOrUndefined } from 'util';
import { RemissionResourceType, RemissionTargetType } from '../../../modules/remission/models/remission-resource-type.model';
import { RemissionService, RemissionProcess } from '@isa/remission';
import { RemissionFinishingProcessStatus } from '../../../modules/remission/models/remission-finishing-process-status.enum';
import get from 'lodash/get';
import { RemissionActiveView } from '../../../modules/remission/models';

export class RemissionStateModel {
  remission: Remission;
  existingRemissions: RemissionExistingOverview;
  isLoading: boolean;
}

@State<RemissionStateModel>({
  name: 'remission',
  defaults: {
    remission: null,
    existingRemissions: null,
    isLoading: true,
  },
})
@Injectable()
export class RemissionState {
  constructor(private store: Store) {}

  @Action(actions.SetRemissionCreated)
  setRemissionCreated(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionCreated) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      remissionProcessCreated: status,
      activeView: RemissionActiveView.PRODUCTS,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionStarted)
  setRemissionStarted(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionStarted) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      remissionProcessStarted: status,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionCompleted)
  setRemissionCompleted(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionCompleted) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      remissionProcessCompleted: status,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionProcess)
  setRemissionProcess(ctx: StateContext<RemissionStateModel>, { remissionProcess, initialValue }: actions.SetRemissionProcess) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const filter = get(remissionProcess, 'filter.filter', null);
    const remissionWithEmptyFilters =
      filter === null ||
      Object.keys(filter).every((sourceKey) =>
        Object.keys(filter[sourceKey]).every((targetKey) => Object.keys(filter[sourceKey][targetKey]).length === 0)
      );
    const remission: Remission = {
      ...currentRemission,
      remissionProcess: {
        ...currentRemission.remissionProcess,
        ...remissionProcess,
        filter: {
          ...get(currentRemission, 'remissionProcess.filter', {}),
          ...remissionProcess.filter,
          filter:
            initialValue && remissionWithEmptyFilters
              ? get(currentRemission, 'remissionProcess.filter.filter', {})
              : remissionProcess.filter.filter,
        },
      },
    };

    const remissionState: RemissionStateModel = {
      ...state,
      remission,
    };

    ctx.patchState(remissionState);
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionFinishedProcessStatus)
  setRemissionFinishedProcessStatus(ctx: StateContext<RemissionStateModel>, { status }: actions.SetRemissionFinishedProcessStatus) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      remissionFinishingProcessStatus: status,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionScannedContainerId)
  setRemissionScannedContainerId(ctx: StateContext<RemissionStateModel>, { containerId }: actions.SetRemissionScannedContainerId) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      containerId: containerId,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionTarget)
  setRemissionTarget(ctx: StateContext<RemissionStateModel>, { target, source }: actions.SetRemissionTarget) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    let remission: Remission;
    remission = {
      ...currentRemission,
      target: target.name,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionSource)
  setRemissionSource(ctx: StateContext<RemissionStateModel>, { source }: actions.SetRemissionSource) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      source,
      remissionProcess: {
        ...currentRemission.remissionProcess,
        filter: {
          ...currentRemission.remissionProcess.filter,
          source,
        },
      },
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionProducts)
  setRemissionProducts(ctx: StateContext<RemissionStateModel>, { products }: actions.SetRemissionProducts) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      remissionProducts: products,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionSearchedProduct)
  setRemissionSearchedProduct(ctx: StateContext<RemissionStateModel>, { product }: actions.SetRemissionSearchedProduct) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      searchedProduct: product,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.ActivateRemissionReminder)
  activateRemissionReminder(ctx: StateContext<RemissionStateModel>) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = { ...currentRemission, blockReminder: false };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.ResetRemissionShippingDocument)
  resetRemissionShippingDocument(ctx: StateContext<RemissionStateModel>) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission: Remission = {
      ...currentRemission,
      shippingDocumentCreated: false,
      remissionFinishingProcessStatus: RemissionFinishingProcessStatus.notSet,
      remissionProcess: {
        ...currentRemission.remissionProcess,
        shippingDocuments: [],
      },
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.CompleteRemissionShippingDocument)
  completeRemissionShippingDocument(ctx: StateContext<RemissionStateModel>, { shippingDocument }: actions.SetRemissionShippingDocument) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const currentRemissionProcess = currentRemission.remissionProcess;
    let updatedRemissionProcess: RemissionProcess = {
      ...currentRemission.remissionProcess,
    };

    if (currentRemissionProcess.shippingDocuments) {
      const shippingDocumentAlreadyExists =
        currentRemissionProcess.shippingDocuments.findIndex((sd) => sd.id === shippingDocument.id) !== -1;
      if (shippingDocumentAlreadyExists) {
        updatedRemissionProcess = {
          ...updatedRemissionProcess,
          shippingDocuments: [
            ...currentRemission.remissionProcess.shippingDocuments.map((ds) => {
              if (ds.id === shippingDocument.id) {
                return {
                  ...ds,
                  isCompleted: true,
                };
              }
              return ds;
            }),
          ],
        };
      }
    }
    const remission: Remission = {
      ...currentRemission,
      remissionFinishingProcessStatus: RemissionFinishingProcessStatus.containerScanned,
      remissionProcess: updatedRemissionProcess,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.DeleteRemissionShippingDocument)
  deleteShippingDocument(ctx: StateContext<RemissionStateModel>, { remissionProcessId }: actions.DeleteRemissionShippingDocument) {
    const state = ctx.getState();
    const currentRemission = state.remission;

    const remission: Remission = {
      ...currentRemission,
      shippingDocument: null,
      shippingDocumentCreated: null,
      containerId: null,
    };

    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.SetRemissionShippingDocument)
  setRemissionShippingDocument(ctx: StateContext<RemissionStateModel>, { shippingDocument }: actions.SetRemissionShippingDocument) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const currentRemissionProcess = currentRemission.remissionProcess;
    let updatedRemissionProcess: RemissionProcess;

    if (currentRemissionProcess.shippingDocuments) {
      const shippingDocumentAlreadyExists =
        currentRemissionProcess.shippingDocuments.findIndex((sd) => sd.id === shippingDocument.id) !== -1;
      if (shippingDocumentAlreadyExists) {
        updatedRemissionProcess = {
          ...currentRemission.remissionProcess,
          shippingDocuments: [
            ...currentRemission.remissionProcess.shippingDocuments.map((ds) => {
              if (ds.id === shippingDocument.id) {
                return shippingDocument;
              }
              return ds;
            }),
          ],
        };
      } else {
        updatedRemissionProcess = {
          ...currentRemission.remissionProcess,
          shippingDocuments: [...(currentRemission.remissionProcess.shippingDocuments || []), shippingDocument],
        };
      }
    } else {
      updatedRemissionProcess = {
        ...currentRemission.remissionProcess,
        shippingDocuments: [...(currentRemission.remissionProcess.shippingDocuments || []), shippingDocument],
      };
    }
    const remission: Remission = {
      ...currentRemission,
      shippingDocument,
      shippingDocumentCreated: true,
      remissionProcess: updatedRemissionProcess,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.InitRemissionState)
  initRemissionState(ctx: StateContext<RemissionStateModel>) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    if (isNullOrUndefined(currentRemission)) {
      const remission = <Remission>{
        remissionProcessCreated: false,
        remissionProcessCompleted: false,
        remissionProcessStarted: false,
        target: RemissionTargetType.BLANK,
        source: RemissionResourceType.Central,
        shippingDocumentCreated: false,
        blockReminder: true,
        remissionFinishingProcessStatus: RemissionFinishingProcessStatus.notSet,
      };
      ctx.patchState({ remission });
      this.syncApiState(remission);
    }
  }

  @Action(actions.ResetRemissionState)
  resetRemissionState(ctx: StateContext<RemissionStateModel>) {
    const state = ctx.getState();
    const currentRemission = state.remission;
    const remission = <Remission>{
      remissionProcessCreated: false,
      remissionProcessCompleted: false,
      remissionProcessStarted: false,
      target: RemissionTargetType.BLANK,
      source: RemissionResourceType.Central,
      shippingDocumentCreated: false,
      blockReminder: true,
      remissionFinishingProcessStatus: RemissionFinishingProcessStatus.notSet,
      remissionProcess: {
        filter: {
          filter: currentRemission.remissionProcess.filter.filter,
        },
      },
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.CompleateRemissionState)
  compleateRemissionState(ctx: StateContext<RemissionStateModel>) {
    const remission = <Remission>{
      remissionProcessCreated: false,
      remissionProcessCompleted: true,
      remissionProcessStarted: false,
      target: RemissionTargetType.BLANK,
      source: RemissionResourceType.Central,
      shippingDocumentCreated: false,
      blockReminder: true,
      remissionFinishingProcessStatus: RemissionFinishingProcessStatus.finished,
    };
    ctx.patchState({ remission });
    this.syncApiState(remission);
  }

  @Action(actions.ReloadRemission)
  reload(ctx: StateContext<RemissionStateModel>, { remission }: actions.ReloadRemission) {
    ctx.patchState({
      remission: {
        ...remission,
        remissionProcess: {
          ...remission.remissionProcess,
        },
      },
    });
  }

  @Action(actions.SetAllOpenExistingRemissions)
  setAllOpenExistingRemissions(ctx: StateContext<RemissionStateModel>, { remissionProcesses }: actions.SetAllOpenExistingRemissions) {
    const state = ctx.getState();
    const currentExistingRemissions = state.existingRemissions;
    const existingRemissions: RemissionExistingOverview = {
      ...currentExistingRemissions,
      openRemissions: remissionProcesses,
    };

    ctx.patchState({ existingRemissions });
  }

  @Action(actions.SetAllExistingRemissions)
  setAllExistingRemissions(ctx: StateContext<RemissionStateModel>, { remissionProcesses }: actions.SetAllExistingRemissions) {
    const state = ctx.getState();
    const currentExistingRemissions = state.existingRemissions;
    const existingRemissions: RemissionExistingOverview = {
      ...currentExistingRemissions,
      allRemissions: remissionProcesses,
    };

    ctx.patchState({ existingRemissions });
  }

  private syncApiState(remission: Remission) {
    const userSyncData: UserStateSyncData = {
      remission,
    };
    const userData = JSON.stringify(userSyncData);
    this.store.dispatch(new AppUserDataSync(userData));
  }

  @Action(actions.SetRemissionFilter)
  setRemissionFilter(ctx: StateContext<RemissionStateModel>, { filter }: actions.SetRemissionFilter) {
    const state = ctx.getState();
    const remissionState = state.remission;

    const remission: Remission = {
      ...remissionState,
      target: filter.target.name,
      source: filter.source as RemissionResourceType,
      remissionProcess: {
        ...remissionState.remissionProcess,
        filter,
      },
    };

    ctx.patchState({
      remission,
    });
    this.syncApiState(remission);
  }

  @Action(actions.RequestUpdateRemissionFilter)
  setIsLoadingFilters(ctx: StateContext<RemissionStateModel>, { filter }: actions.RequestUpdateRemissionFilter) {
    const state = ctx.getState();
    const remissionState = state.remission;

    const remission: Remission = {
      ...remissionState,
      target: (filter.target && filter.target.name) || remissionState.target,
      source: filter.source as RemissionResourceType,
      remissionProcess: {
        ...remissionState.remissionProcess,
        filter: {
          ...remissionState.remissionProcess.filter,
          source: filter.source || remissionState.remissionProcess.filter.source,
          target: filter.target || remissionState.remissionProcess.filter.target,
        },
      },
    };

    ctx.patchState({
      ...state,
      remission,
      isLoading: true,
    });
  }

  @Action(actions.SetRemissionIsLoading)
  setIsLoading(ctx: StateContext<RemissionStateModel>, { isLoading }: actions.SetRemissionIsLoading) {
    const state = ctx.getState();

    ctx.patchState({
      ...state,
      isLoading,
    });
  }

  @Action(actions.SetRemissionActiveView)
  setActiveView(ctx: StateContext<RemissionStateModel>, { activeView }: actions.SetRemissionActiveView) {
    const state = ctx.getState();
    const remissionState = state.remission;

    ctx.patchState({
      ...state,
      remission: {
        ...remissionState,
        activeView,
      },
    });
  }
}
