import { AfterViewInit, ChangeDetectionStrategy, Component, OnDestroy, OnInit, QueryList, ViewChild, ViewChildren } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { Config } from '@core/config';
import { DomainOmsService } from '@domain/oms';
import { OrderItemListItemDTO } from '@swagger/oms';
import { UiFilter } from '@ui/filter';
import { UiScrollContainerComponent } from '@ui/scroll-container';
import { isEqual } from 'lodash';
import { BehaviorSubject, combineLatest, Observable, Subject } from 'rxjs';
import { debounceTime, first, map, shareReplay, takeUntil, tap } from 'rxjs/operators';
import { GoodsInListItemComponent } from './goods-in-list-item/goods-in-list-item.component';
import { GoodsInListStore } from './goods-in-list.store';

@Component({
  selector: 'page-goods-in-list',
  templateUrl: 'goods-in-list.component.html',
  styleUrls: ['goods-in-list.component.scss'],
  providers: [GoodsInListStore],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class GoodsInListComponent implements OnInit, AfterViewInit, OnDestroy {
  @ViewChildren(GoodsInListItemComponent) listItems: QueryList<GoodsInListItemComponent>;

  @ViewChild('scrollContainer', { static: false })
  scrollContainer: UiScrollContainerComponent;

  items$ = this.store.results$;

  itemLength$ = this.items$.pipe(map((items) => items?.length));

  hits$ = this.store.hits$;

  loading$ = this.store.loading$.pipe(shareReplay());

  filter$ = this.store.filter$;

  listEmpty$ = combineLatest([this.loading$, this.hits$]).pipe(
    map(([loading, hits]) => !loading && hits === 0),
    shareReplay()
  );

  editSsc: boolean;

  editSscDisabled$: Observable<boolean>;

  showSaveSscSpinner$ = new BehaviorSubject<boolean>(false);

  private _onDestroy$ = new Subject();

  constructor(
    private _breadcrumb: BreadcrumbService,
    private _domainOmsService: DomainOmsService,
    public store: GoodsInListStore,
    private _router: Router,
    private _route: ActivatedRoute,
    private readonly _config: Config
  ) {}

  ngOnInit() {
    this.store.setTake(Number(this._route.snapshot.queryParams.take ?? 25));
    this._route.queryParams.pipe(takeUntil(this._onDestroy$), debounceTime(0)).subscribe(async (params) => {
      const scrollPos = Number(params.scroll_position ?? 0);

      // Initial Search - Always Search If No Params Are Set
      if (
        (Object.keys(params).length === 0 || this.store.results.length === 0) &&
        Object.keys(this.cleanupQueryParams(params)).length === 0
      ) {
        this.store.search({
          cb: () => {
            setTimeout(() => {
              this.scrollContainer?.scrollTo(scrollPos);
            }, 0);
          },
        });
      }

      if (this.store.filter instanceof UiFilter) {
        await this.updateBreadcrumb(this.store.filter.getQueryParams());
      } else {
        this.store.setDefaultFilter();
      }

      const cleanQueryParams = this.cleanupQueryParams(params);

      if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this.store?.filter?.getQueryParams()))) {
        this.store?.filter?.fromQueryParams(params);
        this.store.search({
          cb: () => {
            setTimeout(() => {
              this.scrollContainer?.scrollTo(scrollPos);
            }, 0);
          },
        });
      }

      await this.updateBreadcrumb({ queryParams: params });
      await this.createBreadcrumb({ queryParams: params });
      await this.removeBreadcrumbs();
    });
  }

  ngOnDestroy(): void {
    this._onDestroy$.next();
    this._onDestroy$.complete();

    this.updateBreadcrumb(this.store.filter.getQueryParams());
  }

  cleanupQueryParams(params: Record<string, string> = {}) {
    const clean = { ...params };
    delete clean['scroll_position'];
    delete clean['take'];

    for (const key in clean) {
      if (Object.prototype.hasOwnProperty.call(clean, key)) {
        if (clean[key] == undefined) {
          delete clean[key];
        }
      }
    }

    return clean;
  }

  ngAfterViewInit(): void {
    this.registerEditSscDisabled();

    this.listItems.changes.pipe(takeUntil(this._onDestroy$)).subscribe(() => this.registerEditSscDisabled());
  }

  navigateToDetails(orderItem: OrderItemListItemDTO) {
    if (this.editSsc) {
      return;
    }

    const customerNumber = orderItem.buyerNumber;
    const orderNumber = orderItem.orderNumber;
    const processingStatus = orderItem.processingStatus;
    const orderItemId = orderItem.orderItemSubsetId;

    this._router.navigate([
      `/filiale/goods/in/details/customer/${encodeURIComponent(customerNumber)}/order/${encodeURIComponent(
        orderNumber
      )}/item/${orderItemId}/${processingStatus}`,
    ]);
  }

  async removeBreadcrumbs() {
    const detailsCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'details'])
      .pipe(first())
      .toPromise();
    const editCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'edit'])
      .pipe(first())
      .toPromise();

    detailsCrumbs.forEach((crumb) => {
      this._breadcrumb.removeBreadcrumb(crumb.id, true);
    });

    editCrumbs.forEach((crumb) => {
      this._breadcrumb.removeBreadcrumb(crumb.id, true);
    });
  }

  async updateBreadcrumb(queryParams: Record<string, string> | Params = this.store.filter?.getQueryParams()) {
    const scroll_position = this.scrollContainer?.scrollPos;
    const take = this._route?.snapshot?.queryParams?.take;

    if (queryParams) {
      const crumbs = await this._breadcrumb
        .getBreadcrumbsByKeyAndTags$(this._config.get('process.ids.goodsIn'), ['goods-in', 'list'])
        .pipe(first())
        .toPromise();
      const params = { ...queryParams, scroll_position, take };

      for (const crumb of crumbs) {
        this._breadcrumb.patchBreadcrumb(crumb.id, {
          params,
        });
      }
    }
  }

  async createBreadcrumb(queryParams: Params) {
    await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists({
      key: this._config.get('process.ids.goodsIn'),
      name: 'Wareneingangsliste',
      path: '/filiale/goods/in/list',
      section: 'branch',
      params: queryParams,
      tags: ['goods-in', 'list'],
    });
  }

  registerEditSscDisabled() {
    this.editSscDisabled$ = combineLatest([
      combineLatest(this.listItems.map((item) => item.sscInvalid$)),
      combineLatest(this.listItems.map((item) => item.sscChanged$)),
    ]).pipe(map(([sscInvalid, sscChanged]) => sscInvalid.includes(true) || sscChanged.every((item) => !item)));
  }

  async loadMore() {
    if (this.store.hits > this.store.results.length && !this.store.loading) {
      this.store.search({});
    }
  }

  async saveSsc() {
    this.showSaveSscSpinner$.next(true);

    const payload = this.listItems
      .filter((item) => item.sscChanged)
      .map((listItem) => {
        return { id: listItem.item.orderItemSubsetId, ssc: listItem.ssc, quantity: listItem.item.quantity };
      });
    if (payload?.length > 0) {
      try {
        await this._domainOmsService.changeStockStatusCode(payload).toPromise();
      } catch (error) {
        console.error(error);
      }
    }
    this.refreshList();
    this.showSaveSscSpinner$.next(false);
    this.editSsc = false;
  }

  cancelSsc() {
    this.editSsc = false;
    this.listItems.forEach((listItem) => listItem.sscUpdated());
  }

  refreshList() {
    this.store.reload();
  }
}
