import { Component, OnInit, Input, ViewChild, ChangeDetectorRef, OnDestroy, Output, EventEmitter } from '@angular/core';
import { ModalService, SearchInputComponent } from '@libs/ui';
import { ItemDTO } from '@swagger/cat';
import { BranchSelectors } from 'apps/sales/src/app/core/store/selectors/branch.selector';
import { Observable, Subject, of } from 'rxjs';
import { BranchDTO } from '@swagger/checkout';
import { Select, Store } from '@ngxs/store';
import { tap, map, switchMap, takeUntil, filter } from 'rxjs/operators';
import { ProductAvailabilityService } from 'apps/sales/src/app/core/services/product-availability.service';
import { isNullOrUndefined } from 'util';
import { distance, undefinedToEmptyStringOrValue } from 'apps/sales/src/app/core/utils/app.utils';
import { BranchService } from 'apps/sales/src/app/core/services/branch.service';

@Component({
  selector: 'app-branches-avalability-overview',
  templateUrl: './branches-avalability-overview.component.html',
  styleUrls: ['./branches-avalability-overview.component.scss'],
})
export class BranchesAvalabilityOverviewComponent implements OnInit, OnDestroy {
  id = 'branches-overview-modal';
  @Select(BranchSelectors.getBranchesIterable) branches$: Observable<BranchDTO[]>;

  branchInfos: BranchInfoView[] = [];
  filteredBranchInfos: BranchInfoView[] = [];
  userBranch: BranchDTO;
  @Output() destroymodal: EventEmitter<boolean> = new EventEmitter();

  destroy$ = new Subject();

  @Input() item: ItemDTO;
  @ViewChild('searchInput') searchInput: SearchInputComponent;

  error: string;

  constructor(
    private modalService: ModalService,
    private availabilityService: ProductAvailabilityService,
    private store: Store,
    private branchService: BranchService
  ) {}

  ngOnInit() {
    this.branches$
      .pipe(
        takeUntil(this.destroy$),
        filter((branches) => {
          if (branches && branches.length > 0) {
            return true;
          } else {
            return false;
          }
        }),
        map((branches) => {
          return branches.filter((branch) => {
            if (
              branch &&
              !isNullOrUndefined(branch.isOnline) &&
              branch.isOnline &&
              !isNullOrUndefined(branch.isShippingEnabled) &&
              branch.isShippingEnabled &&
              !isNullOrUndefined(branch.isOrderingEnabled) &&
              branch.isOrderingEnabled
            ) {
              return true;
            } else {
              return false;
            }
          });
        }),
        switchMap((branches) => {
          const userBranchNumber = this.store.selectSnapshot(BranchSelectors.getUserBranch);
          this.userBranch = branches.find((t) => t.branchNumber === userBranchNumber);
          return this.loadBranches(branches);
        })
      )
      .subscribe((t) => {
        this.branchInfos = t.filter((b) => b && b.load);
        this.filteredBranchInfos = this.loadDefaultBranches();
      });
  }

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

  private loadDefaultBranches() {
    return this.branchInfos
      .sort((a: BranchInfoView, b: BranchInfoView) => (this.userBranch ? this.branchSorterFn(a.branch, b.branch, this.userBranch) : 1))
      .filter((branch) => (this.userBranch ? branch.branch.branchNumber !== this.userBranch.branchNumber : true))
      .slice(0, 8);
  }

  private branchSorterFn(a: BranchDTO, b: BranchDTO, userBranch: BranchDTO) {
    const userLat = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.latitude : 0;
    const userLong = userBranch.address && userBranch.address.geoLocation ? userBranch.address.geoLocation.longitude : 0;
    const aLat = a.address && a.address.geoLocation ? a.address.geoLocation.latitude : 0;
    const aLong = a.address && a.address.geoLocation ? a.address.geoLocation.longitude : 0;
    const bLat = b.address && b.address.geoLocation ? b.address.geoLocation.latitude : 0;
    const bLong = b.address && b.address.geoLocation ? b.address.geoLocation.longitude : 0;
    const aDist = distance(userLat, userLong, aLat, aLong);
    const bDist = distance(userLat, userLong, bLat, bLong);
    return aDist - bDist;
  }

  loadBranches(branches: BranchDTO[]) {
    const branchIds = branches.map((branch) => branch.id);
    return this.availabilityService.getStockInfo(branchIds, [this.item.id]).pipe(
      map((response) => {
        return response.map((stockInfo, i) => {
          const branch = branches.find((t) => t.id === stockInfo.branchId);
          if (branch) {
            return <BranchInfoView>{
              name: branch.shortName,
              address:
                undefinedToEmptyStringOrValue(branch.address.street) +
                undefinedToEmptyStringOrValue(branch.address.streetNumber) +
                ', ' +
                undefinedToEmptyStringOrValue(branch.address.zipCode) +
                ' ' +
                undefinedToEmptyStringOrValue(branch.address.city),
              filter: branch.address.zipCode + branch.address.city,
              availability: stockInfo.inStock,
              branch: branch,
              load: !isNullOrUndefined(branch.shortName) && !isNullOrUndefined(branch.address.zipCode),
            };
          }
          const fallbackBranch = branches[i];
          if (fallbackBranch) {
            return <BranchInfoView>{
              name: fallbackBranch.shortName,
              address:
                undefinedToEmptyStringOrValue(fallbackBranch.address.street) +
                undefinedToEmptyStringOrValue(fallbackBranch.address.streetNumber) +
                ', ' +
                undefinedToEmptyStringOrValue(fallbackBranch.address.zipCode) +
                ' ' +
                undefinedToEmptyStringOrValue(fallbackBranch.address.city),
              filter: fallbackBranch.address.zipCode + fallbackBranch.address.city,
              availability: 0,
              branch: fallbackBranch,
              load: !isNullOrUndefined(fallbackBranch.shortName) && !isNullOrUndefined(fallbackBranch.address.zipCode),
            };
          }
        });
      })
    );
  }

  openDialog() {
    this.modalService.open(this.id);
  }

  closeDialog() {
    this.error = undefined;
    this.modalService.close(this.id);
    this.filter(null);
    this.searchInput.clearInput();
    this.destroymodal.emit(true);
  }

  filter(searchTerm: string) {
    if (isNullOrUndefined(searchTerm) || searchTerm.length < 1) {
      this.filteredBranchInfos = this.loadDefaultBranches();
      this.error = undefined;
      return;
    }
    this.branchService
      .searchBranches(searchTerm.trim(), 0, 8)
      .pipe(
        takeUntil(this.destroy$),
        switchMap((branches) => {
          if (branches && branches.length > 0) {
            return this.loadBranches(branches.filter((branch) => branch.branchNumber !== this.userBranch.branchNumber));
          }
          return of(null);
        })
      )
      .subscribe((t) => {
        if (t) {
          this.error = undefined;
          const branchInfos = t.filter((b) => b && b.load);
          this.filteredBranchInfos = branchInfos
            .sort((a: BranchInfoView, b: BranchInfoView) => this.branchSorterFn(a.branch, b.branch, this.userBranch))
            .slice(0, 8);
        } else {
          this.error = 'Ergibt keine Suchergebnisse';
          this.filteredBranchInfos = [];
        }
      });
  }
}

export interface BranchInfoView {
  name: string;
  address: string;
  filter: string;
  availability: number;
  load: boolean;
  branch: BranchDTO;
}
