import { Injectable } from '@angular/core';
import { Params } from '@msfa-models/api/params.model';
import { AvropCompact, AvropCompactData } from '@msfa-models/avrop.model';
import { Handledare } from '@msfa-models/handledare.model';
import { AvropApiService } from '@msfa-services/api/avrop-api.service';
import { MultiselectFilterOption } from '@msfa-shared/components/multiselect/multiselect-filter-option';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { filter, map, switchMap, tap } from 'rxjs/operators';

type Step = 1 | 2 | 3 | 4;

@Injectable({
  providedIn: 'root',
})
export class AvropService {
  private _limit$ = new BehaviorSubject<number>(5);
  private _page$ = new BehaviorSubject<number>(1);
  private _filteredTjanster$ = new BehaviorSubject<MultiselectFilterOption[]>(null);
  private _filteredUtforandeVerksamheter$ = new BehaviorSubject<MultiselectFilterOption[]>(null);
  private _filteredKommuner$ = new BehaviorSubject<MultiselectFilterOption[]>(null);
  private _selectedAvrop$ = new BehaviorSubject<AvropCompact[]>([]);
  private _avropIsLocked$ = new BehaviorSubject<boolean>(null);
  private _selectedHandledareId$ = new BehaviorSubject<string>(null);
  private _handledareIsConfirmed$ = new BehaviorSubject<boolean>(false);
  private _avropIsSubmitted$ = new BehaviorSubject<boolean>(false);
  private _error$ = new BehaviorSubject<string>(null);
  private _avropLoading$ = new BehaviorSubject<boolean>(false);
  public avropLoading$: Observable<boolean> = this._avropLoading$.asObservable();

  public filteredTjanster$: Observable<MultiselectFilterOption[]> = this._filteredTjanster$.asObservable();
  public filteredUtforandeVerksamheter$: Observable<
    MultiselectFilterOption[]
  > = this._filteredUtforandeVerksamheter$.asObservable();
  public filteredKommuner$: Observable<MultiselectFilterOption[]> = this._filteredKommuner$.asObservable();
  public selectedAvrop$: Observable<AvropCompact[]> = this._selectedAvrop$.asObservable();
  public avropIsLocked$: Observable<boolean> = this._avropIsLocked$.asObservable();
  public selectedHandledare$: Observable<Handledare> = this._selectedHandledareId$.pipe(
    filter(selectedHandledare => !!selectedHandledare),
    switchMap(handledareId =>
      this.availableHandledare$.pipe(
        map(availableHandledare => availableHandledare.find(handledare => handledare.ciamUserId === handledareId))
      )
    )
  );
  public handledareIsConfirmed$: Observable<boolean> = this._handledareIsConfirmed$.asObservable();
  public avropIsSubmitted$: Observable<boolean> = this._avropIsSubmitted$.asObservable();
  public error$: Observable<string> = this._error$.asObservable();

  public avropData$: Observable<AvropCompactData> = combineLatest([
    this._filteredTjanster$,
    this._filteredUtforandeVerksamheter$,
    this._filteredKommuner$,
    this._page$,
    this._limit$,
  ]).pipe(
    switchMap(() => {
      this._avropLoading$.next(true);
      return this.avropApiService.fetchAvrop$(this.filtersForAvrop).pipe(
        tap(() => {
          this._avropLoading$.next(false);
        })
      );
    })
  );

  public availableTjanster$: Observable<MultiselectFilterOption[]> = combineLatest([
    this._filteredUtforandeVerksamheter$,
    this._filteredKommuner$,
  ]).pipe(switchMap(() => this.avropApiService.fetchAvailableTjanster$(this.filtersForTjanster)));

  public availableUtforandeVerksamheter$: Observable<MultiselectFilterOption[]> = combineLatest([
    this._filteredTjanster$,
    this._filteredKommuner$,
  ]).pipe(
    switchMap(() => this.avropApiService.fetchAvailableUtforandeVerksamheter$(this.filtersForUtforandeVerksamheter))
  );

  public availableKommuner$: Observable<MultiselectFilterOption[]> = combineLatest([
    this._filteredTjanster$,
    this._filteredUtforandeVerksamheter$,
  ]).pipe(switchMap(() => this.avropApiService.fetchAvailableKommuner$(this.filtersForKommuner)));

  private _lockedAvrop$: Observable<AvropCompact[]> = combineLatest([this.selectedAvrop$, this.avropIsLocked$]).pipe(
    map(([selectedAvrop, isLocked]) => (isLocked ? selectedAvrop : null))
  );

  public availableHandledare$: Observable<Handledare[]> = this._lockedAvrop$.pipe(
    filter(lockedAvrop => !!lockedAvrop?.length),
    switchMap(lockedAvrop => this.avropApiService.fetchAvailableHandledare$(lockedAvrop))
  );

  public currentStep$: Observable<Step> = combineLatest([
    this._handledareIsConfirmed$,
    this._avropIsLocked$,
    this._avropIsSubmitted$,
  ]).pipe(
    map(([confirmedHandledare, avropIsLocked, avropIsSubmitted]) => {
      return AvropService.calculateStep(confirmedHandledare, avropIsLocked, avropIsSubmitted);
    })
  );

  private static calculateStep(confirmedHandledare: boolean, avropIsLocked: boolean, avropIsSubmitted: boolean): Step {
    if (avropIsLocked) {
      if (confirmedHandledare) {
        if (avropIsSubmitted) {
          return 4;
        }
        return 3;
      }
      return 2;
    }

    return 1;
  }

  private get tjanstKoder(): { tjanstKoder: string[] } | null {
    return this._filteredTjanster$.value?.length
      ? { tjanstKoder: this._filteredTjanster$.value.map(tjanst => tjanst.id) }
      : null;
  }

  private get kommunKoder(): { kommunKoder: string[] } | null {
    return this._filteredKommuner$.value?.length
      ? { kommunKoder: this._filteredKommuner$.value.map(kommun => kommun.id) }
      : null;
  }

  private get utforandeverksamhetIds(): { utforandeverksamhetIds: string[] } | null {
    return this._filteredUtforandeVerksamheter$.value?.length
      ? {
          utforandeverksamhetIds: this._filteredUtforandeVerksamheter$.value.map(
            utforandeVerksamhet => utforandeVerksamhet.id
          ),
        }
      : null;
  }

  private get filtersForAvrop(): Params {
    return {
      ...this.tjanstKoder,
      ...this.kommunKoder,
      ...this.utforandeverksamhetIds,
      page: this._page$.value.toString(),
      limit: this._limit$.value.toString(),
    };
  }

  private get filtersForTjanster(): Params {
    return {
      ...this.kommunKoder,
      ...this.utforandeverksamhetIds,
    };
  }

  private get filtersForUtforandeVerksamheter(): Params {
    return {
      ...this.tjanstKoder,
      ...this.kommunKoder,
    };
  }

  private get filtersForKommuner(): Params {
    return {
      ...this.tjanstKoder,
      ...this.utforandeverksamhetIds,
    };
  }

  public setSelectedAvrop(deltagare: AvropCompact[]): void {
    this._selectedAvrop$.next(deltagare);
  }

  constructor(private avropApiService: AvropApiService) {}

  public resetError(): void {
    this._error$.next(null);
  }

  public lockSelectedAvrop(): void {
    this.resetError();
    if ((this._selectedAvrop$?.value?.length ?? -1) <= 0) {
      this._error$.next('För att låsa deltagare behöver några ha markerats först.');
      return;
    }
    this._avropIsLocked$.next(true);
  }

  public unlockSelectedAvrop(): void {
    this.resetError();
    this._avropIsLocked$.next(false);
  }

  public confirmHandledare(): void {
    this.resetError();
    if (!this._selectedHandledareId$?.value) {
      this._error$.next('För att kunna tilldela behövs en handledare väljas först.');
      return;
    }
    this._handledareIsConfirmed$.next(true);
  }

  public unconfirmHandledare(): void {
    this.resetError();
    this._handledareIsConfirmed$.next(false);
  }

  public async save(): Promise<void> {
    this.resetError();
    if (!this._handledareIsConfirmed$) {
      this._error$.next('Handledaren måste bekräftas innan avropet kan sparas');
      return;
    }

    if (!this._avropIsLocked$) {
      this._error$.next('Deltagarlistan måste låsas innan avropet kan sparas');
      return;
    }

    await this.avropApiService.assignHandledare(this._selectedAvrop$.value, this._selectedHandledareId$.value);
    this._avropIsSubmitted$.next(true);
  }

  public assignHandledare(handledareId: string): void {
    this._selectedHandledareId$.next(handledareId);
  }

  public setSelectedTjanster(selectedFilterOptions: MultiselectFilterOption[]): void {
    this._filteredTjanster$.next(selectedFilterOptions);
  }

  public setSelectedUtforandeVerksamheter(selectedFilterOptions: MultiselectFilterOption[]): void {
    this._filteredUtforandeVerksamheter$.next(selectedFilterOptions);
  }

  public setSelectedKommuner(selectedFilterOptions: MultiselectFilterOption[]): void {
    this._filteredKommuner$.next(selectedFilterOptions);
  }

  public goToStep1(): void {
    this._selectedHandledareId$.next(null);
    this._selectedAvrop$.next([]);
    this._avropIsLocked$.next(false);
    this._handledareIsConfirmed$.next(false);
  }

  public removeKommun(kommunToRemove: MultiselectFilterOption): void {
    this.setSelectedKommuner(this._filteredKommuner$.value.filter(selectedKommun => selectedKommun !== kommunToRemove));
  }

  public removeUtforandeVerksamhet(utforandeVerksamhetToRemove: MultiselectFilterOption): void {
    this.setSelectedUtforandeVerksamheter(
      this._filteredUtforandeVerksamheter$.value.filter(
        selectedUtforandeVerksamhet => selectedUtforandeVerksamhet !== utforandeVerksamhetToRemove
      )
    );
  }

  public removeTjanst(tjanstToRemove: MultiselectFilterOption): void {
    this.setSelectedTjanster(this._filteredTjanster$.value.filter(selectedTjanst => selectedTjanst !== tjanstToRemove));
  }

  public setPage(page: number): void {
    this._page$.next(page);
  }
}
