import { ChangeDetectionStrategy, Component, EventEmitter, OnDestroy, OnInit, Output, ViewChild } from '@angular/core';
import { ActivatedRoute } from '@angular/router';
import { BreadcrumbService } from '@core/breadcrumb';
import { ApplicationService } from '@core/application';
import { DomainCatalogService } from '@domain/catalog';
import { combineLatest, NEVER, Subscription } from 'rxjs';
import { catchError, debounceTime, first, switchMap, map, tap } from 'rxjs/operators';
import { ArticleSearchService } from '../article-search.store';
import { isEqual } from 'lodash';
import { EnvironmentService } from '@core/environment';
import { Filter, FilterInputGroupMainComponent } from '@shared/components/filter';
import { ProductCatalogNavigationService } from '@shared/services/navigation';

@Component({
  selector: 'page-article-search-main',
  templateUrl: 'search-main.component.html',
  styleUrls: ['search-main.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
  standalone: false,
})
export class ArticleSearchMainComponent implements OnInit, OnDestroy {
  readonly history$ = this.catalog.getSearchHistory({ take: 7 }).pipe(
    map((history) => history.filter((h) => !!h.friendlyName)),
    catchError(() => NEVER),
  );

  fetching$ = this.searchService.fetching$;

  filter$ = this.searchService.filter$;

  searchboxHint$ = this.searchService.searchboxHint$;

  subscriptions = new Subscription();

  hasFilter$ = combineLatest([this.searchService.filter$, this.searchService.defaultSettings$]).pipe(
    map(([filter, defaultFilter]) => {
      const filterQueryParams = filter?.getQueryParams();
      return !isEqual(
        this.resetQueryParamsQueryAndOrderBy(filterQueryParams),
        Filter.create(defaultFilter).getQueryParams(),
      );
    }),
  );

  @Output() showFilter = new EventEmitter<void>();

  @ViewChild(FilterInputGroupMainComponent, { static: false })
  sharedFilterInputGroupMain: FilterInputGroupMainComponent;

  get isDesktop$() {
    return this._environment.matchDesktopLarge$;
  }

  constructor(
    private searchService: ArticleSearchService,
    private catalog: DomainCatalogService,
    private route: ActivatedRoute,
    private application: ApplicationService,
    private breadcrumb: BreadcrumbService,
    private _environment: EnvironmentService,
    private _navigationService: ProductCatalogNavigationService,
  ) {}

  ngOnInit() {
    this.subscriptions.add(
      combineLatest([this.application.activatedProcessId$, this.route.queryParams])
        .pipe(debounceTime(0))
        .subscribe(async ([processId, queryParams]) => {
          const processChanged = processId !== this.searchService.processId;

          if (!(this.searchService.filter instanceof Filter)) {
            await this.searchService.setDefaultFilter();
          }

          if (processChanged) {
            this.updateBreadcrumb(this.searchService.processId, this.searchService.filter.getQueryParams());

            this.searchService.setProcess(processId);
          }

          const cleanQueryParams = this.cleanupQueryParams(queryParams);

          if (!isEqual(cleanQueryParams, this.cleanupQueryParams(this.searchService.filter.getQueryParams()))) {
            // Reset Filter on Product Search Shell Navigation click
            await this.searchService.setDefaultFilter(queryParams);
          }

          this.removeResultsAndDetailsBreadcrumbs(processId);

          // #4519 und #4520 Durch den Performance Umbau, wird auf allen größen kleiner als der Splitscreen die Article-Search Komponente nicht geladen
          // Stattdessen werden die Komponenten search-main und search-filter geladen. Einige Funktionen von Article-Search müssen trotzdem aufgerufen werden
          if (!this._environment.matchDesktopLarge()) {
            this.resetFilter(queryParams);
            this.removeCheckoutBreadcrumb(processId);
            this.addOrUpdateBreadcrumbs(processId, queryParams);
          }
        }),
    );

    this.subscriptions.add(
      this.application.activatedProcessId$
        .pipe(
          debounceTime(0),
          switchMap((processId) => this.application.getSelectedBranch$(processId)),
        )
        .subscribe((selectedBranch) => {
          const branchChanged = selectedBranch?.id !== this.searchService?.selectedBranch?.id;
          if (branchChanged) {
            this.searchService.setBranch(selectedBranch);
          }
        }),
    );

    // #4143 To make Splitscreen Search and Filter work combined
    this.subscriptions.add(
      this.searchService.searchStarted.subscribe(async (_) => {
        const queryParams = {
          ...this.cleanupQueryParams(this.searchService.filter.getQueryParams()),
          main_qs: this.sharedFilterInputGroupMain?.uiInput?.value,
        };
        await this.searchService.setDefaultFilter(queryParams);
      }),
    );
  }

  ngOnDestroy() {
    this.subscriptions.unsubscribe();

    this.updateBreadcrumb(this.searchService.processId, this.searchService.filter.getQueryParams());
  }

  search(filter: Filter) {
    this.sharedFilterInputGroupMain.cancelAutocomplete();
    this.searchService.setFilter(filter);
    this.searchService.search({ clear: true });
  }

  setQueryHistory(filter: Filter, query: string) {
    filter.fromQueryParams({ main_qs: query });
  }

  resetQueryParamsQueryAndOrderBy(params: Record<string, string> = {}) {
    const clean = { ...params };

    for (const key in clean) {
      if (key === 'main_qs' || key?.includes('order_by')) {
        clean[key] = undefined;
      } else if (key?.includes('order_by')) {
        delete clean[key];
      }
    }

    return clean;
  }

  cleanupQueryParams(params: Record<string, string> = {}) {
    const clean = { ...params };

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

    return clean;
  }

  async updateBreadcrumb(processId: number, queryParams: Record<string, string>) {
    const crumbs = await this.breadcrumb
      .getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'main'])
      .pipe(first())
      .toPromise();

    crumbs.forEach((crumb) => {
      this.breadcrumb.patchBreadcrumb(crumb.id, {
        params: queryParams,
      });
    });
  }

  resetFilter(queryParams: Record<string, string>) {
    if (Object.keys(queryParams).length === 0) {
      this.searchService.resetFilter();
    }
  }

  async removeCheckoutBreadcrumb(processId: number) {
    this.breadcrumb.removeBreadcrumbsByKeyAndTags(processId, ['checkout']);
  }

  async addOrUpdateBreadcrumbs(processId: number, queryParams: Record<string, string>) {
    await this.breadcrumb.addBreadcrumbIfNotExists({
      key: processId,
      name: 'Artikelsuche',
      path: this._navigationService.getArticleSearchBasePath(processId).path,
      params: queryParams,
      tags: ['catalog', 'main'],
      section: 'customer',
    });
  }

  async removeResultsAndDetailsBreadcrumbs(processId: number) {
    const resultsCrumbs = await this.breadcrumb
      .getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'results'])
      .pipe(first())
      .toPromise();
    const detailCrumbs = await this.breadcrumb
      .getBreadcrumbsByKeyAndTags$(processId, ['catalog', 'details'])
      .pipe(first())
      .toPromise();

    resultsCrumbs?.forEach((crumb) => {
      this.breadcrumb.removeBreadcrumb(crumb.id);
    });

    detailCrumbs?.forEach((crumb) => {
      this.breadcrumb.removeBreadcrumb(crumb.id);
    });
  }
}
