import { Component, ChangeDetectionStrategy, OnInit, ViewChild, OnDestroy, AfterViewInit } from '@angular/core';
import { ActivatedRoute, Params, Router } from '@angular/router';
import { Breadcrumb, BreadcrumbService } from '@core/breadcrumb';
import { CacheService } from '@core/cache';
import { Config } from '@core/config';
import { provideComponentStore } from '@ngrx/component-store';
import { ArrivalStatus, ListResponseArgsOfPackageDTO2 } from '@generated/swagger/wws-api';
import { UiFilter } from '@ui/filter';
import moment from 'moment';
import { combineLatest, Subject, Subscription } from 'rxjs';
import { filter, first, map } from 'rxjs/operators';
import { PackageResultCacheData } from './package-result-cache-data';
import { PackageResultComponentStore } from './package-result.component.store';
import { SharedFilterOverlayComponent } from '@shared/components/filter-overlay';
import { PackageListComponent } from '@shared/components/package-list';
import { PackageInspectionNavigationService } from '../services/package-inspection-navigation.service';

@Component({
  selector: 'page-package-result',
  templateUrl: 'package-result.component.html',
  styleUrls: ['package-result.component.css'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  providers: [provideComponentStore(PackageResultComponentStore)],
  standalone: false,
})
export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy {
  private _subscription = new Subscription();

  get breadcrumbKey(): string {
    return this._config.get('process.ids.packageInspection');
  }

  hint$ = new Subject<string>();

  @ViewChild(SharedFilterOverlayComponent)
  filterOverlay: SharedFilterOverlayComponent;

  /**
   * Zeigt die liste an, wenn entweder keine packages geladen werden oder wenn packages geladen wurden
   * und mindestens ein package vorhanden ist.
   */
  showList$ = combineLatest([this.store.fetching$, this.store.packages$]).pipe(
    map(([fetching, packages]) => fetching || packages.length > 0),
  );

  @ViewChild(PackageListComponent, { static: true, read: PackageListComponent })
  packageList: PackageListComponent;

  initialScrollIndex = 0;

  constructor(
    public store: PackageResultComponentStore,
    private _activatedRoute: ActivatedRoute,
    private _config: Config,
    private _breadcrumb: BreadcrumbService,
    private _router: Router,
    private _cache: CacheService,
    private _navigation: PackageInspectionNavigationService,
  ) {}

  ngOnInit(): void {
    this.removeBreadcrumbs();
  }

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

    this.initFetchResponseSubscription();
  }

  hasUpdate(): { arrivalStatus: ArrivalStatus; packageId: string } | undefined {
    const packageId = this._activatedRoute.snapshot.queryParams['updated_packageId'];
    const arrivalStatus = this._activatedRoute.snapshot.queryParams['updated_arrivalStatus'];
    if (!!packageId && !!arrivalStatus) {
      return { arrivalStatus: Number(arrivalStatus) as ArrivalStatus, packageId: String(packageId) };
    }
  }

  initFilterSubscription() {
    const initialFilter$ = this.store.filter$.pipe(
      filter((f) => f instanceof UiFilter),
      first(),
    );
    const queryParams$ = this._activatedRoute.queryParams;

    const filterSub = combineLatest([initialFilter$, queryParams$]).subscribe(async ([filter, queryParams]) => {
      const restoredFilter = this.restoreFilterFromQueryParams(filter, queryParams);
      const restoredData = await this.restoreResultsFromCache(restoredFilter);
      this.createBreadcrumbIfNotExists(restoredFilter || this.store.filter);
      this.fetchPackages(restoredFilter, { keep: true, take: restoredData?.packages?.length });
    });

    this._subscription.add(filterSub);
  }

  initFetchResponseSubscription() {
    const onFetchPackageResponseSub = this.store.onFetchPackagesResponse$.subscribe(this.onFetchPackagesResponse);

    this._subscription.add(onFetchPackageResponseSub);
  }

  ngOnDestroy(): void {
    this._subscription.unsubscribe();
  }

  restoreFilterFromQueryParams(filter: UiFilter, queryParams: Params): UiFilter {
    const nextFilter = UiFilter.create(filter);
    nextFilter.fromQueryParams(queryParams);
    this.store.setFilter(nextFilter);
    this.store.setPendingFilter(nextFilter);
    return nextFilter;
  }

  async restoreResultsFromCache(filter: UiFilter): Promise<PackageResultCacheData | undefined> {
    const data = await this._cache.get<PackageResultCacheData>(filter.getQueryParams());
    if (data) {
      const update = this.hasUpdate();

      if (update) {
        const packageIndex = data.packages.findIndex((p) => p.id === update.packageId);
        if (packageIndex > -1) {
          data.packages[packageIndex].arrivalStatus = update.arrivalStatus;
        }
      }

      this.store.setPackages(data.packages);
      this.store.setTotal(data.total);
      this.store.setFetching(false);

      if (data.scrollIndex) {
        this.initialScrollIndex = data.scrollIndex;
      }
    }

    return data;
  }

  applyFilter() {
    this.store.applyPendingFilterToFilter();
    this.fetchPackages(this.store.filter);
  }

  fetchPackages(filter: UiFilter, options: { keep?: boolean; take?: number } = {}) {
    this.hint$.next('');
    this.store.fetchPackages(options);
  }

  onFetchPackagesResponse = (response: ListResponseArgsOfPackageDTO2) => {
    this.patchLocation(this.store.filter);

    this.createBreadcrumbIfNotExists(this.store.filter);
    if (response.error) {
      console.error(response);
      return;
    }

    if (response.result.length === 0) {
      this.hint$.next('Keine Pakete gefunden');
      return;
    } else if (response.result.length === 1) {
      this._navigation.details(response.result[0].id).navigate();
    }

    this.cacheData({});
    this.closeFilterOverlay();
  };

  openFilter() {
    this.store.cancelFetchPackages();
    this.filterOverlay.open();
  }

  resetFilter() {
    this.store.resetPendingFilter();
    this.store.cancelFetchPackages();
  }

  async patchLocation(filter: UiFilter): Promise<void> {
    this._router.navigate([], {
      queryParams: filter.getQueryParams(),
    });
  }

  getBreadcrumbName(filter: UiFilter): string {
    const params = filter.getQueryParams();
    const mainQs = params['main_qs'];

    let name: string[] = [];

    if (mainQs) {
      name.push(mainQs);
    }

    const filterLieferant = params['filter_lieferant'];
    if (filterLieferant) {
      name.push(filterLieferant.split(';').join(' & '));
    }

    if (name.length > 0) {
      return name.join(' - ');
    }

    const filterZeitraum = params['filter_zeitraum'];

    if (filterZeitraum) {
      const range = filterZeitraum.split('"-"').map((s) => s.replace('"', ''));
      if (range[0] && range[1]) {
        const start = moment(range[0]).format('DD.MM.YYYY');
        const end = moment(range[1]).format('DD.MM.YYYY');
        return `${start} - ${end}`;
      } else if (filterZeitraum.endsWith('"-')) {
        const dateString = filterZeitraum.replace('"-', '').replace('"', '');
        return `Ab ${moment(dateString).format('DD.MM.YYYY')}`;
      } else if (filterZeitraum.startsWith('-"')) {
        const dateString = filterZeitraum.replace('-"', '').replace('"', '');
        return `Bis ${moment(dateString).format('DD.MM.YYYY')}`;
      }
    }

    return 'Paket-Übersicht';
  }

  async createBreadcrumbIfNotExists(filter: UiFilter): Promise<void> {
    const crumb: Breadcrumb = {
      key: this.breadcrumbKey,
      name: this.getBreadcrumbName(filter),
      path: '/filiale/package-inspection',
      section: 'branch',
      params: filter.getQueryParams(),
      tags: ['filter'],
    };

    await this._breadcrumb.addOrUpdateBreadcrumbIfNotExists(crumb);
  }

  async removeBreadcrumbs(): Promise<void> {
    const detailsCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTag$(this.breadcrumbKey, 'details')
      .pipe(first())
      .toPromise();
    const filterCrumbs = await this._breadcrumb
      .getBreadcrumbsByKeyAndTag$(this.breadcrumbKey, 'filter')
      .pipe(first())
      .toPromise();
    const crumbs = [...detailsCrumbs, ...filterCrumbs];
    for (let crumb of crumbs) {
      await this._breadcrumb.removeBreadcrumb(crumb.id);
    }
  }

  closeFilterOverlay() {
    this.store.restorePendingFilter();
    this.filterOverlay.close();
  }

  allItemsRendered() {
    this.store.paginatePackages();
  }

  scrollIndexChange($event: number) {
    this.cacheData({ scrollIndex: $event });
  }

  cacheData(partial: Partial<PackageResultCacheData>) {
    this._cache.set(this.store.filter.getQueryParams(), {
      packages: this.store.packages,
      total: this.store.total,
      ...partial,
    } as PackageResultCacheData);
  }
}
