import { Component, OnInit, OnDestroy, ViewChild, ElementRef, ChangeDetectionStrategy } from '@angular/core';
import { Subject, Observable } from 'rxjs';
import { takeUntil, filter, take, switchMap, catchError, first, map, withLatestFrom, tap } from 'rxjs/operators';
import { RemissionResourceType } from '../../models/remission-resource-type.model';

import { Select, Store } from '@ngxs/store';
import { RemissionSelectors } from 'apps/sales/src/app/core/store/selectors/remission.selectors';
import { RemissionProcessStatuses } from 'apps/sales/src/app/core/models/remission-process-statuses.model';
import { RESOURCE_TYPE_SWITCH } from '../../constants/remission.constants';
import { isNullOrUndefined } from 'util';
import {
  SetRemissionProcess,
  SetRemissionCreated,
  SetRemissionStarted,
  SetRemissionSearchedProduct,
  ActivateRemissionReminder,
  SetAllOpenExistingRemissions,
  SetRemissionSource,
  SetRemissionTarget,
  RequestUpdateRemissionFilter,
  SetRemissionIsLoading,
} from 'apps/sales/src/app/core/store/actions/remission.actions';
import { RemissionHelperService } from '../../services/remission-helper.service';
import { RemissionListComponent } from '../../components/remission-list/remission-list.component';
import { AddBreadcrumb, ResetBreadcrumbsTo } from 'apps/sales/src/app/core/store/actions/breadcrumb.actions';
import { Router, ActivatedRoute, Params } from '@angular/router';
import { SetBranchProcessCurrentPath } from 'apps/sales/src/app/core/store/actions/branch-process.actions';
import { RemissionStartDialogComponent } from '../../components/remission-start-dialog/remission-start-dialog.component';
import { AppService } from 'apps/sales/src/app/core/services/app.service';
// tslint:disable-next-line: max-line-length
import { AddProductToRemissionDialogComponent } from '../../components/add-product-to-remission-dialog/add-product-to-remission-dialog.component';
// tslint:disable-next-line: max-line-length
import { RemissionToTopToBottomActionsComponent } from '../../components/remission-to-top-to-bottom-actions/remission-to-top-to-bottom-actions.component';
// tslint:disable-next-line: max-line-length
import { RemissionScanProductInvalidBarcodeComponent } from '../../components/remission-scan-product-invalid-barcode/remission-scan-product-invalid-barcode.component';
import { NativeContainerService } from 'shared/lib/remission-container-scanner/native-container.service';
import { SupplierDTO } from '@cmf/inventory-api';
import { fadeInWithDelay } from '../../animations/fadeIn.animation';
import {
  RemissionProcess,
  RemissionProduct,
  RemissionSelectedFilters,
  RemissionService,
  RemissionSourceType,
  RemissionSupplier,
} from '@isa/remission';

@Component({
  selector: 'app-remission-list-create',
  templateUrl: './remission-list-create.component.html',
  styleUrls: ['./remission-list-create.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  animations: [fadeInWithDelay(0.5)],
})
export class RemissionListCreateComponent implements OnInit, OnDestroy {
  @Select(RemissionSelectors.getRemissionProcessStatuses)
  remissionProcessStatuses$: Observable<RemissionProcessStatuses>;
  @Select(RemissionSelectors.getRemissionFilters)
  remissionFilters$: Observable<RemissionSelectedFilters>;
  @Select(RemissionSelectors.getRemissionProcess)
  currentRemissionProcess$: Observable<RemissionProcess>;
  @Select(RemissionSelectors.getRemissionSource)
  currentRemissionSource$: Observable<RemissionResourceType>;
  @Select(RemissionSelectors.getRemissiontarget)
  currentRemissionTarget$: Observable<string>;
  @Select(RemissionSelectors.getRemissionActiveFilters)
  activeFilters$: Observable<{ [key: string]: string[] }>;
  @Select(RemissionSelectors.getIsLoading)
  isLoading$: Observable<boolean>;

  remissionProcess$: Observable<RemissionProcess>;
  remissionSuppliers$ = this.remissionService.getRemissionTargets();
  remissionTargets$ = this.remissionService.getRemissionTargets();
  remissionListHits$: Observable<number>;
  listLoaded$: Observable<boolean>;
  showCtAs$: Observable<boolean>;

  @ViewChild('toTopToBottomActions')
  toTopToBottomActions: RemissionToTopToBottomActionsComponent;
  @ViewChild('remissionList')
  remissionList: RemissionListComponent;
  @ViewChild('remissionListContainer')
  remissionListContainer: ElementRef;
  @ViewChild('remissionStartDialog')
  remissionStartDialog: RemissionStartDialogComponent;
  @ViewChild('addProductToRemissionDialog')
  addProductToRemissionDialog: AddProductToRemissionDialogComponent;
  @ViewChild('invalidBarcodeDialog')
  invalidBarcodeDialog: RemissionScanProductInvalidBarcodeComponent;
  destroy$ = new Subject();

  searchedRemissionProcess: RemissionProcess;
  resourceTypeSwitch = RESOURCE_TYPE_SWITCH;
  remissionProcessStatuses: RemissionProcessStatuses;
  processNeedsToBeRestoredFromCache = false;
  hits: number;
  listLoaded = false;
  isIPad = false;
  isSafari = false;
  isNative = false;
  searchedRemissionProduct: RemissionProduct;
  filtersExpanded = false;
  filtersActive = false;
  private resetFilters = false;

  constructor(
    private remissionService: RemissionService,
    private store: Store,
    private remissionHelper: RemissionHelperService,
    private router: Router,
    private appService: AppService,
    private route: ActivatedRoute,
    private nativeContainerService: NativeContainerService
  ) {}

  ngOnInit() {
    this.initialisations();
    this.subscriptions();
  }

  initialisations() {
    this.detectDevice();

    this.route.queryParams
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        take(1)
      )
      .subscribe(this.queryParamsSubscriptionHandler);

    this.continueRemissionProcess();
    this.initCtAs();
  }

  detectDevice() {
    this.isIPad = this.appService.isIPadEnv();
    const { isSafari, isNative } = this.nativeContainerService.isUiWebview();
    this.isSafari = isSafari;
    this.isNative = isNative;
  }

  initCtAs() {
    this.showCtAs$ = this.activeFilters$.pipe(
      withLatestFrom(this.currentRemissionSource$),
      map(([filters, source]) => {
        if (source === 'zentral') {
          return true;
        }

        return !isNullOrUndefined(filters) && !!Object.values(filters).length && !!(filters.abteilungen && filters.abteilungen.length);
      })
    );
  }

  onFilterClicked() {
    this.filtersActive = !this.filtersActive;
  }

  queryParamsSubscriptionHandler = (params: Params) => {
    if (params['mode'] === 'addProduct') {
      this.getCurrentSearchedRemissionProduct();
      this.resetFilters = true;
    }
  };

  getCurrentSearchedRemissionProduct() {
    this.store
      .select(RemissionSelectors.getRemissionSearchedProduct)
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        take(1)
      )
      .subscribe((remissionProduct) => {
        this.searchedRemissionProduct = remissionProduct;
        setTimeout(() => {
          this.addProductToRemissionDialog.openDialog();
        }, 40);
      });
  }

  subscriptions() {
    this.remissionSubsribtionInitialisation();
    this.initRemissionListHits();
    this.initFiltersSync();
  }

  remissionSubsribtionInitialisation() {
    return this.remissionProcessStatuses$
      .pipe(
        takeUntil(this.destroy$),
        filter((data) => !isNullOrUndefined(data)),
        switchMap((status) => {
          let process$: Observable<RemissionProcess>;

          if (status.started && !status.completed) {
            this.navigateToStartedRemission();
          }

          if (!status.created) {
            process$ = this.createProcess();
          } else {
            process$ = this.continueProcess();
          }

          return process$.pipe(filter((process) => !isNullOrUndefined(process)));
        }),
        take(1)
      )
      .subscribe((process) => {
        this.store.dispatch(new SetRemissionProcess(process));
        this.loadUncompletedRemissions();
      });
  }

  remissionCreatedSuccesfullyHandler = (process: RemissionProcess) => {
    if (process) {
      this.store.dispatch(new SetRemissionCreated(true));
      this.store.dispatch(new SetRemissionProcess(process));
    }
  };

  loadUncompletedRemissions() {
    return this.remissionService
      .getAllRemissions({
        showOpen: true,
        showCompleted: false,
        take: 200,
      })
      .pipe(
        takeUntil(this.destroy$),
        filter((remissionProcesses) => !isNullOrUndefined(remissionProcesses))
      )
      .subscribe(this.uncompletedRemissionsSubscriptionHandler);
  }

  uncompletedRemissionsSubscriptionHandler = (remissionProcesses: RemissionProcess[]) => {
    this.store.dispatch(new SetAllOpenExistingRemissions(remissionProcesses));
  };

  continueProcess(): Observable<RemissionProcess> {
    return this.currentRemissionProcess$.pipe(filter((data) => !isNullOrUndefined(data), take(1))).pipe(
      switchMap((process) => {
        return this.remissionService.continueProcess(process);
      }),
      catchError(this.continueProcessErrorHandler)
    );
  }

  continueProcessErrorHandler = (error: any) => {
    console.error(error);
    this.processNeedsToBeRestoredFromCache = true;
    return this.createProcess();
  };

  createProcess(): Observable<RemissionProcess> {
    this.store.dispatch(new SetRemissionCreated(true));
    return this.remissionService.createProcess();
  }

  remissionSourcesFilter = (remissionSources: RemissionSourceType[]) => {
    return remissionSources && Array.isArray(remissionSources) && remissionSources.length === 2;
  };

  remissionSuppliersFilter = (remissionSuppliers: RemissionSupplier[]) => {
    return remissionSuppliers && Array.isArray(remissionSuppliers) && remissionSuppliers.length === 2;
  };

  remissionSuppliersSubscriptionHandler = (process) => {
    this.remisionProcessSubscriptionHandler(process);
  };

  remisionProcessSubscriptionHandler = (process: RemissionProcess) => {
    this.store
      .dispatch(new SetRemissionProcess(process))
      .toPromise()
      .then(() => {
        this.store.dispatch(new ActivateRemissionReminder());
      });
    this.resetFilters = false;
  };

  startRemission() {
    try {
      throw new Error('[Remission Create] Start Remission');
    } catch (error) {
      console.error(error);
    }
    this.currentRemissionProcess$
      .pipe(
        switchMap((process) =>
          this.remissionService.startRemission({ remissionProcessId: process.id }).pipe(filter((data) => !isNullOrUndefined(data)))
        ),
        first(),
        tap((process) => console.log('[Remission Create] Start Remission II', process))
      )
      .subscribe((process) => {
        this.store.dispatch(new SetRemissionStarted(true));
        this.store.dispatch(new SetRemissionProcess(process));
        this.navigateToStartedRemission();
      });
  }

  openStartRemissionDialog() {
    if (this.isIPad) {
      this.startRemission();
    } else {
      this.remissionStartDialog.openDialog();
    }
  }

  navigateToStartedRemission() {
    const path = '/remission/started';
    this.store.dispatch(
      new AddBreadcrumb(
        {
          path: path,
          name: 'Warenbegleitschein',
        },
        'remission'
      )
    );
    this.store.dispatch(new SetBranchProcessCurrentPath(path));
    this.router.navigate([path]);
  }

  openAddProductToRemission() {
    this.navigateToAddProductToRemission();
  }

  filterToggled(state: boolean) {
    if (state) {
      setTimeout(() => {
        // HIMA-1045 smooth scroll the expanded options into view
        const container = document.getElementById('options');
        if (container) {
          container.scrollIntoView({ block: 'end', behavior: 'smooth' });
        }
      }, 300);
    }
  }

  navigateToAddProductToRemission() {
    const path = '/remission/search';
    this.store.dispatch(
      new AddBreadcrumb(
        {
          path: path,
          name: 'Wannennummer',
        },
        'remission'
      )
    );
    this.router.navigate([path]);
    this.store.dispatch(new SetBranchProcessCurrentPath(path));
  }

  addProductToRemissionList(product: RemissionProduct) {
    this.searchedRemissionProduct = undefined;
    this.resetFilters = true;

    this.currentRemissionProcess$
      .pipe(
        take(1),
        map((process) => process.id),
        withLatestFrom(this.currentRemissionTarget$, this.currentRemissionSource$),
        catchError((error) => {
          this.store.dispatch(new SetRemissionIsLoading(false));
          return [error];
        })
      )
      .subscribe(([remissionProcessId, target, source]) => {
        const targetSource = 'zentral';
        let newTarget = target;
        if (this.isValidSupplierDTO(product.target)) {
          newTarget = product.target;
        }

        const targetHasChanged = newTarget.name !== target;
        const sourceHasChanged = targetSource !== source;

        if (targetHasChanged || sourceHasChanged) {
          this.store.dispatch(
            new RequestUpdateRemissionFilter(remissionProcessId, {
              source: targetSource,
              target: newTarget,
            })
          );
        }
      });

    this.navigateRemissionCreateList();
  }

  private isValidSupplierDTO(supplier: Partial<SupplierDTO>): boolean {
    return !!supplier.id && !!supplier.name && !!supplier.supplierNumber;
  }

  closeAddProductToRemissionDialog() {
    this.searchedRemissionProduct = undefined;
    this.navigateRemissionCreateList();
  }

  navigateRemissionCreateList() {
    const path = '/remission/create';
    this.store.dispatch(
      new ResetBreadcrumbsTo(
        {
          path: path,
          name: 'Remission',
        },
        'remission'
      )
    );
    this.router.navigate([path]);
    this.store.dispatch(new SetBranchProcessCurrentPath(path, true));
  }

  searchProductScannedResult(param: string) {
    if (param) {
      if (!param || param.length !== 13) {
        this.invalidBarcodeDialog.openDialog();
        return;
      }
      this.searchProduct(param);
    }
  }

  searchProduct(input: string) {
    this.remissionService
      .searchProduct({ ean: input })
      .pipe(take(1))
      .subscribe(this.searchProductResultHandler, () => this.invalidBarcodeDialog.openDialog());
  }

  searchProductResultHandler = (remissionProduct: RemissionProduct) => {
    if (isNullOrUndefined(remissionProduct)) {
      this.store.dispatch(new SetRemissionSearchedProduct(undefined));
      this.invalidBarcodeDialog.openDialog();
      return;
    }
    this.store.dispatch(new SetRemissionSearchedProduct(remissionProduct));
    this.searchedRemissionProduct = remissionProduct;
    setTimeout(() => {
      this.addProductToRemissionDialog.openDialog();
    }, 40);
  };

  ngOnDestroy() {
    this.destroy$.next();
  }

  private continueRemissionProcess() {
    this.currentRemissionProcess$
      .pipe(
        filter((process) => !isNullOrUndefined(process)),
        switchMap((process) => {
          const isExisting = this.isExisting(process);
          if (isExisting) {
            return this.remissionService.continueProcess(process);
          }

          return this.remissionService.createProcess(process);
        }),
        first()
      )
      .subscribe((process) => this.syncContinuedProcessWithStore(process));
  }

  private isExisting(process: RemissionProcess) {
    if (!process.filter) {
      return false;
    }

    return Object.keys(process.filter).length && process.filter.source && process.filter.target;
  }

  private syncContinuedProcessWithStore(process: RemissionProcess) {
    this.store.dispatch(new SetRemissionProcess(process, true));
    this.store.dispatch(new SetRemissionSource(process.filter.source as RemissionResourceType));
    this.store.dispatch(new SetRemissionTarget(process.filter.target, process.filter.source as RemissionResourceType));
    this.store.dispatch(new ActivateRemissionReminder());
  }

  private initRemissionListHits() {
    this.remissionListHits$ = this.remissionHelper.remissionListHits$.pipe(filter((hits) => !isNullOrUndefined(hits)));
  }

  private initFiltersSync() {
    this.currentRemissionProcess$
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        takeUntil(this.destroy$)
      )
      .subscribe((process) =>
        this.remissionHelper.updateFilters({
          remissionProcessId: process.id,
        })
      );
  }
}
