import { ProductService } from '../../../../core/services/product.service';
import {
  Component,
  OnInit,
  ViewChild,
  OnDestroy,
  ChangeDetectionStrategy,
  ChangeDetectorRef,
  AfterViewInit,
  ViewChildren,
  QueryList,
} from '@angular/core';
import { Search } from '../../../../core/models/search.model';
import { Process } from '../../../../core/models/process.model';
import { Product } from '../../../../core/models/product.model';
import { Select, Store } from '@ngxs/store';
import { Observable, Subject } from 'rxjs';
import { staggerAnimation } from './stagger.animation';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { SearchDataSource } from './product-data.datasource';
import { takeUntil, filter, take, distinctUntilChanged, switchMap, map } from 'rxjs/operators';
import { SharedSelectors } from '../../../../core/store/selectors/shared.selectors';
import { ProcessSelectors } from '../../../../core/store/selectors/process.selectors';
import { isNullOrUndefined } from 'util';
import { PRODUCT_SCROLL_INDEX } from '../../../../core/utils/app.constants';
import { FilterItem } from '../../../../core/models/filter-item.model';
import { Filter } from '../../../../core/models/filter.model';
import { ProductSerachFilters } from '../../../../core/models/product-search-filters.model';
import { RadioButtonGroup } from '@libs/ui/lib/radio-button-group';
import { SetProcessProductFilters, AddSearch } from '../../../../core/store/actions/process.actions';
import { ProductUtilService } from 'apps/sales/src/app/core/services/product-util.service';
import { Side } from '@libs/ui/lib/small-double-choice-switch';
import { LoadFilters, SetFilterType, ClearNegativeFilters } from 'apps/sales/src/app/core/store/actions/filter.actions';
import { FilterType } from 'apps/sales/src/app/core/models/filter-type.enum';
import { ItemDTO } from '@swagger/cat';
import { ProductCardComponent } from '../../components/product-card/product-card.component';
import { ProductSearchResult } from 'apps/sales/src/app/core/models/product-search-result.model';

@Component({
  selector: 'app-product-results',
  templateUrl: './product-results.component.html',
  styleUrls: ['./product-results.component.scss'],
  animations: [staggerAnimation],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class ProductResultsComponent implements OnInit, OnDestroy, AfterViewInit {
  @Select(ProcessSelectors.getProcesses) processes$: Observable<Process[]>;
  currentProcess: Process;
  @Select(SharedSelectors.getSearchedProducts) products$: Observable<ProductSearchResult>;
  @Select(ProcessSelectors.getProductFilters) filters$: Observable<ProductSerachFilters>;
  id: number;
  currentSearch: Search;
  products: Product[];
  skip = 0;
  processCount = 0;
  @ViewChild('scroller') scroller: CdkVirtualScrollViewport;
  @ViewChildren('productCard') productCardList: QueryList<ProductCardComponent>;
  loading = true;
  filters: Filter[];
  scrollDisabled = false;
  filterShowMore = false;
  loadCountOfPage = 0;
  searchFired = false;
  doNotFireNegativeSearch = false;
  doScroll = true;

  dummies = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10];
  ds: SearchDataSource;
  destroy$ = new Subject();

  selectedFilterMode: Side;

  radioButtonGroup: RadioButtonGroup;
  acrhiveFilter: boolean;

  timoutScheisseUmwerk: any;

  get areFiltersNegative() {
    if (this.selectedFilterMode) {
      return this.selectedFilterMode === Side.RIGHT;
    }
    const currentFilterType = this.store.selectSnapshot(SharedSelectors.getFilterType);
    return currentFilterType ? currentFilterType === FilterType.Negative : false;
  }

  get filterContainerHeight() {
    if (document.getElementsByClassName('filter-container')[0]) {
      const height = document.getElementsByClassName('filter-container')[0].clientHeight + 285;
      return `calc(100% - ${height}px)`;
    }
    return '0px';
  }

  constructor(
    private store: Store,
    private productService: ProductService,
    private cdr: ChangeDetectorRef,
    private productUtils: ProductUtilService
  ) {}

  ngOnInit() {
    this.store
      .select(ProcessSelectors.getCurrentProcess)
      .pipe(
        distinctUntilChanged((prev, curr) => prev && curr && JSON.stringify(prev) === JSON.stringify(curr)),
        takeUntil(this.destroy$),
        filter((data) => !isNullOrUndefined(data) && data.id !== this.id),
        switchMap((data) =>
          this.store.select(SharedSelectors.getFilterType).pipe(
            filter((fType) => !isNullOrUndefined(fType)),
            takeUntil(this.destroy$),
            map((fType) => {
              return { filterType: fType, process: data };
            })
          )
        )
      )
      .subscribe(({ filterType, process }) => {
        this.currentProcess = process;
        this.currentSearch = process.search;
        this.id = process.id;
        this.selectedFilterMode = filterType === FilterType.Negative ? Side.RIGHT : Side.LEFT;

        if (this.selectedFilterMode === Side.RIGHT && this.doNotFireNegativeSearch) {
          return;
        }

        if (this.currentSearch) {
          this.loadCountOfPage += 1;
          this.loadDataSource(false);
          if (this.loadCountOfPage === 1) {
            this.store.dispatch(new AddSearch({ ...this.currentSearch, firstLoad: false }));
          }
        }
      });

    this.filters$
      .pipe(
        filter((data) => !isNullOrUndefined(data)),
        take(1)
      )
      .subscribe((filters: ProductSerachFilters) => {
        this.radioButtonGroup = filters.radioButtonGroup;
        this.acrhiveFilter = filters.archiveFilters;
      });

    this.productUtils.scrollDisabled$
      .pipe(
        takeUntil(this.destroy$),
        filter((data) => !isNullOrUndefined(data))
      )
      .subscribe((disable) => (this.scrollDisabled = disable));
  }

  ngAfterViewInit() {
    if (this.scroller) {
      this.scroller.renderedRangeStream
        .pipe(
          takeUntil(this.destroy$),
          filter((data) => !isNullOrUndefined(data))
        )
        .subscribe(() => {
          if (this.doScroll) {
            this.scrollToIndexCached();
          }
        });
    }
  }

  initiateDataSource(search: Search, filters: any[], areFiltersNegative: boolean, skipCache: boolean) {
    this.ds = new SearchDataSource(this.productService, search, this.store, filters, areFiltersNegative, skipCache);
  }

  loadDataSource(skipCache: boolean) {
    this.initiateDataSource(
      this.currentSearch,
      this.currentSearch.fitlers ? this.currentSearch.fitlers : [],
      this.areFiltersNegative,
      skipCache
    );

    // Load products up to the previous page
    const [pageSize] = this.getSelectedProductScrollInformation();
    this.ds.getProductsUpTo(pageSize);

    this.loading = false;
    this.cdr.detectChanges();

    this.timoutScheisseUmwerk = setTimeout(() => {
      this.cdr.detectChanges();
      this.scroller.checkViewportSize();
    }, 500);
  }

  saveProductPositionHandler(index) {
    const scrollPosition = this.scroller.getOffsetToRenderedContentStart();
    const pageSize = Math.max(Math.ceil(this.scroller.getRenderedRange().end / SearchDataSource.pageSize), 0);
    sessionStorage.setItem(PRODUCT_SCROLL_INDEX, pageSize + ':' + index + ':' + scrollPosition);
  }

  scrollToIndexCached() {
    setTimeout(() => {
      const [, index, offset] = this.getSelectedProductScrollInformation();
      if (offset !== 0) {
        // Scroll to the last scroll position
        this.scroller.scrollToOffset(offset);
      }
      if (index) {
        // Scroll to the selected product - sometimes it can take some time to find it
        const scrollToInterval = setInterval(() => {
          const found = this.productCardList.find((productCard) => productCard.index === index);
          if (found) {
            found.elementRef.nativeElement.scrollIntoView();
            clearInterval(scrollToInterval);
          }
        }, 250);
      }
      this.doScroll = false;
    }, 1);
  }

  getSelectedProductScrollInformation() {
    const index = sessionStorage.getItem(PRODUCT_SCROLL_INDEX);
    if (!isNullOrUndefined(index)) {
      const ids = index.split(':');
      return [+ids[0], +ids[1], +ids[2]];
    }
    return [0, 0, 0];
  }

  updateSearch() {
    const fil = this.store.selectSnapshot(SharedSelectors.getProcessFilters);
    if (fil) {
      const currentSearch = this.store.selectSnapshot(SharedSelectors.getCurrentSearch);
      this.loading = true;
      this.filters = fil;
      const type =
        this.radioButtonGroup.radioButtons && this.radioButtonGroup.radioButtons.find((t) => t.selected)
          ? this.radioButtonGroup.radioButtons.find((t) => t.selected).key
          : undefined;
      const search = { ...currentSearch, type: type };
      this.initiateDataSource(search, !isNullOrUndefined(fil) ? fil : [], this.areFiltersNegative, true);
      this.loading = false;
    }
    setTimeout(() => {
      this.cdr.detectChanges();
      this.scroller.checkViewportSize();
    }, 500);
  }

  authorFilter(author: string) {
    // Needed for virtual scroller
    this.loading = true;

    // Select author filter in UI
    this.selectAuthorFilter();

    this.currentSearch = <Search>{
      query: author,
      fitlers: this.currentSearch.fitlers ? this.currentSearch.fitlers : [],
      take: 5,
      skip: 0,
      firstLoad: false,
      type: 'author',
      archiveFilter: this.currentSearch.archiveFilter,
    };
    this.store.dispatch(new AddSearch(this.currentSearch));
    if (!this.searchFired) {
      this.searchFired = true;
      this.initiateDataSource(this.currentSearch, [], this.areFiltersNegative, true);
      this.loading = false;
      this.searchFired = false;
      this.cdr.detectChanges();
    }
  }

  filtersClicked(filtersData: ProductSerachFilters) {
    if (this.radioButtonGroup) {
      this.loading = true;
      this.radioButtonGroup = filtersData.radioButtonGroup;
      this.acrhiveFilter = filtersData.archiveFilters;
      const filters = this.filters ? this.filters : this.activeFilters();
      const type =
        this.radioButtonGroup.radioButtons && this.radioButtonGroup.radioButtons.find((t) => t.selected)
          ? this.radioButtonGroup.radioButtons.find((t) => t.selected).key
          : undefined;

      const exists: Search = {
        query: this.currentSearch.query,
        skip: 0,
        take: 5,
        fitlers: filters,
        firstLoad: false,
        type: type,
        archiveFilter: this.acrhiveFilter,
      };
      this.store.dispatch(new AddSearch(exists));

      this.initiateDataSource(exists, filters, this.areFiltersNegative, true);
      this.loading = false;
      this.cdr.detectChanges();

      // HIMA-1106 - Fixing the change detection on the scroller not displaying full search results
      setTimeout(() => {
        this.cdr.detectChanges();
        this.scroller.checkViewportSize();
      }, 500);
    }
  }

  private activeFilters(): Filter[] {
    let filters: Filter[] = [];
    if (this.currentSearch.fitlers) {
      filters = this.currentSearch.fitlers.map((f: Filter) =>
        f.id === 'cattype'
          ? {
              ...f,
              items: f.items.map((fi: FilterItem) =>
                fi.id === '2' ? { ...fi, selected: this.acrhiveFilter } : { ...fi, selected: false }
              ),
            }
          : f
      );
    }
    return filters;
  }

  private selectAuthorFilter() {
    if (this.radioButtonGroup) {
      let radioButtons = this.radioButtonGroup.radioButtons;
      radioButtons = radioButtons.map((radio) => (radio.key === 'author' ? { ...radio, selected: true } : { ...radio, selected: false }));

      const updatedRadios: RadioButtonGroup = { radioButtons };
      this.radioButtonGroup = updatedRadios;
      this.store.dispatch(new SetProcessProductFilters({ radioButtonGroup: updatedRadios, archiveFilters: this.acrhiveFilter }));
    }
  }

  changeSelectedFilterMode(side: Side) {
    this.selectedFilterMode = side;
    if (side === Side.RIGHT) {
      this.doNotFireNegativeSearch = true;
      this.store
        .dispatch(new ClearNegativeFilters())
        .pipe(
          switchMap(() => this.store.dispatch(new SetFilterType(FilterType.Negative))),
          switchMap(() => this.store.dispatch(new LoadFilters(FilterType.Negative)))
        )
        .toPromise()
        .then(() => {
          this.clearSearch();
        });
    } else {
      this.doNotFireNegativeSearch = true;
      this.store
        .dispatch(new SetFilterType(FilterType.Full))
        .pipe(switchMap(() => this.store.dispatch(new LoadFilters(FilterType.Full))))
        .toPromise()
        .then(() => {
          this.doNotFireNegativeSearch = false;
          this.updateSearch();
        });
    }
  }

  clearSearch() {
    this.doNotFireNegativeSearch = false;
    const currentSearch = this.store.selectSnapshot(SharedSelectors.getCurrentSearch);
    this.loading = true;
    const type =
      this.radioButtonGroup.radioButtons && this.radioButtonGroup.radioButtons.find((t) => t.selected)
        ? this.radioButtonGroup.radioButtons.find((t) => t.selected).key
        : undefined;
    const search = { ...currentSearch, type: type };
    if (!this.searchFired) {
      this.searchFired = true;
      this.initiateDataSource(search, [], false, true);
      this.loading = false;
      this.searchFired = false;
    }
    setTimeout(() => {
      this.cdr.detectChanges();
      this.scroller.checkViewportSize();
    }, 500);
  }

  filterMoreToggle(showMore: boolean) {
    this.filterShowMore = showMore;
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    clearTimeout(this.timoutScheisseUmwerk);
  }
}
