import { Logger } from "src/app/service/logger.service";
import { EventEmitter, Injectable } from "@angular/core";
import { BehaviorSubject, take } from "rxjs";
import { Filter } from "src/app/model/graphql/filter/filter.model";
import { Sort } from "src/app/model/graphql/filter/sort.model";
import { ActivatedRoute, Router } from "@angular/router";
import { TranslateService } from "@ngx-translate/core";

const log = new Logger("filterService");

export type RowsPerPageOption = number | { showAll: string };

@Injectable({
  providedIn: "root",
})
export class FilterService {
  protected useQueryParams = false;

  get rowsPerPageOptions() {
    return [10, 25, 50, { showAll: this.translate.instant("common.all") }];
  }

  filter$ = new BehaviorSubject<Filter[]>([]);
  sort$ = new BehaviorSubject<Sort[]>([]);
  skip$ = new BehaviorSubject<number>(0);
  take$ = new BehaviorSubject<RowsPerPageOption>(this.rowsPerPageOptions[0]);
  onLoad = new EventEmitter<void>();

  constructor(
    private route: ActivatedRoute,
    private router: Router,
    private translate: TranslateService,
  ) {
    this.skip$.subscribe(() => {
      this.setQueryParams();
    });
    this.take$.subscribe((x) => {
      if (x && Object.prototype.hasOwnProperty.call(x, "showAll")) {
        this.take$.next(0);
        return;
      }
      this.setQueryParams();
    });
    this.sort$.subscribe(() => {
      this.setQueryParams();
    });
    this.filter$.subscribe(() => {
      this.setQueryParams();
    });
  }

  load() {
    this.onLoad.emit();
  }

  reset(args?: { withQueryParams: boolean; withHideDeleted?: boolean }) {
    log.debug("Reset", args);
    this.useQueryParams = args?.withQueryParams ?? false;
    if (!this.useQueryParams) {
      this.clearQueryParams();
    }

    this.resetSkip();
    this.resetTake();
    this.resetSortAndFilter();

    if (args?.withHideDeleted) {
      this.filter$.next([{ deleted: { equal: false } }]);
    }

    this.load();
  }

  private resetSkip() {
    if (!this.useQueryParams) {
      this.skip$.next(0);
      return;
    }

    this.skip$.next(this.getQueryValueFromParams("skip") ?? 0);
  }

  private resetTake() {
    if (!this.useQueryParams) {
      this.take$.next(this.rowsPerPageOptions[0]);
      return;
    }

    this.take$.next(
      this.getQueryValueFromParams("take") ?? this.rowsPerPageOptions[0],
    );
  }

  private resetSortAndFilter() {
    this.sort$.next(this.getQueryValueFromParams("sort") ?? []);
    this.filter$.next(this.getQueryValueFromParams("filter") ?? []);
  }

  protected getQueryValueFromParams(value: string) {
    const params = this.route.snapshot.queryParamMap;
    const queryValue = params?.get(value);
    return queryValue ? JSON.parse(queryValue) : undefined;
  }

  protected clearQueryParams() {
    this.router
      .navigate([], {
        relativeTo: this.route,
        queryParams: {
          skip: undefined,
          take: undefined,
          sort: undefined,
          filter: undefined,
        },
        queryParamsHandling: "merge", // remove to replace all query params by provided
        replaceUrl: true,
      })
      .then();
  }

  protected setQueryParams() {
    if (!this.useQueryParams) return;
    log.debug("SetQueryParams");
    log.trace("skip: ", this.skip$.value);
    log.trace("take: ", this.take$.value);
    log.trace("sort: ", this.sort$.value);
    log.trace("filter: ", this.filter$.value);
    this.route.paramMap.pipe(take(1)).subscribe((params) => {
      if (
        params.get("skip") !== this.skip$.value.toString() ||
        params.get("take") !== this.take$.value.toString() ||
        params.get("sort") !== JSON.stringify(this.sort$.value) ||
        params.get("filter") !== JSON.stringify(this.filter$.value)
      )
        this.router
          .navigate([], {
            relativeTo: this.route,
            queryParams: {
              skip: this.skip$.value,
              take: this.take$.value,
              sort:
                this.sort$.value.length > 0
                  ? JSON.stringify(this.sort$.value)
                  : undefined,
              filter:
                this.filter$.value.length > 0
                  ? JSON.stringify(this.filter$.value)
                  : undefined,
            },
            queryParamsHandling: "merge", // remove to replace all query params by provided
            replaceUrl: true,
          })
          .then();
    });
  }
}
