import { DataSource, CollectionViewer } from '@angular/cdk/collections';
import { User } from '../../../../core/models/user.model';
import { Observable, BehaviorSubject, Subject } from 'rxjs';
import { takeUntil, debounceTime, take } from 'rxjs/operators';
import { Store } from '@ngxs/store';
import { CurrentCustomerPageLoaded, AddCustomers } from '../../../../core/store/actions/customer.actions';
import { CustomerService } from '../../../../core/services/customer.service';
import { CustomerSearchResponse } from '../../../../core/models/customer-search-response.model';
import { CustomerFilters } from './customer-search-result.component';
import { AddBreadcrumbNoDub } from '../../../../core/store/actions/breadcrumb.actions';
import { isNullOrUndefined } from 'util';
import { CustomerSelectors } from 'apps/sales/src/app/core/store/selectors/customer.selectors';

export class CustomerSearchDataSource extends DataSource<User | undefined> {
  private pageSize = 10;
  public cachedData = Array.from<User>({ length: 0 });
  private fetchedPages = new Set<number>();
  private dataStream = new BehaviorSubject<(User | undefined)[]>(this.cachedData);
  destroy$ = new Subject();
  public loading = true;
  public results = false;
  public firstLoad = true;
  public allHits = 0;

  constructor(
    private search: string,
    private store: Store,
    private customerService: CustomerService,
    private customerFilters: CustomerFilters,
    private userCount: number = 0,
    private scan = false,
    private first = false
  ) {
    super();
  }

  connect(collectionViewer: CollectionViewer): Observable<(User | undefined)[]> {
    collectionViewer.viewChange.pipe(takeUntil(this.destroy$)).subscribe((range) => {
      const startPage = this.getPageForIndex(range.start);
      const endPage = this.getPageForIndex(range.end - 1);
      for (let i = startPage; i <= endPage; i++) {
        this.fetchPage(i);
      }
    });

    this.dataStream.pipe(takeUntil(this.destroy$), debounceTime(1000)).subscribe((i) => {
      this.store.dispatch(new AddCustomers([...i]));
    });

    this.fetchPage(0);
    return this.dataStream;
  }

  disconnect(): void {
    this.destroy$.next();
  }

  public getPageForIndex(index: number): number {
    return Math.floor(index / this.pageSize);
  }

  private fetchPage(page: number): number {
    this.store.dispatch(new CurrentCustomerPageLoaded(page));

    if (page === 0) {
      this.results = false;
    }
    if (this.fetchedPages.has(page)) {
      return;
    }
    this.fetchedPages.add(page);
    this.loading = true;
    const customerSource$ = !this.scan
      ? page === 0 && this.first
        ? this.store.select(CustomerSelectors.getCachedCustomers)
        : this.customerService.searchCustomer(this.search, false, page * this.pageSize, this.pageSize, false, this.customerFilters)
      : this.customerService.scannCustomer(this.search);
    customerSource$.pipe(take(1)).subscribe((data: CustomerSearchResponse) => {
      if (!data) {
        return;
      }
      this.loading = false;
      this.results = true;
      if (this.allHits === 0) {
        this.store.dispatch(
          new AddBreadcrumbNoDub(
            {
              name:
                (this.search.length > 12 ? this.search.substring(0, 12) + '...' : this.search) +
                ` (${!isNullOrUndefined(data.hits) ? data.hits : data.customers.length} Ergebnisse)`,
              path: '/customer/results',
            },
            'customer'
          )
        );
      }

      if (page === 0) {
        this.cachedData = Array.from<User>({ length: data.hits });
        this.allHits = data.hits;
      }

      this.cachedData.splice(page * this.pageSize, this.pageSize, ...data.customers);
      this.dataStream.next(this.cachedData);

      if (page === 0) {
        // dispatch immediately on first page load
        this.firstLoad = false;
        this.store.dispatch(new AddCustomers([...this.cachedData]));
      }
    });
  }
}
