import { ChangeDetectionStrategy, Component } from '@angular/core';
import { FormControl, FormGroup } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';
import { ANNAN_ORSAK_ID } from '@msfa-constants/franvaro-reasons';
import { OrsaksKoderKanda } from '@msfa-enums/orsaks-koder-kanda.enum';
import { FranvaroReportRequest } from '@msfa-models/api/franvaro-request.model';
import { DeltagareAvrop } from '@msfa-models/avrop.model';
import { CustomError } from '@msfa-models/error/custom-error';
import { FranvaroReason, setKnownReason, sortFranvaroReasons } from '@msfa-models/franvaro-reason.model';
import { Franvaro } from '@msfa-models/franvaro.model';
import { formatDate } from '@msfa-utils/format-to-date.util';
import { Radiobutton } from '@ui/radiobutton-group/radiobutton.model';
import { addDays } from 'date-fns';
import { BehaviorSubject, combineLatest, Observable } from 'rxjs';
import { map, shareReplay, switchMap, take } from 'rxjs/operators';
import { FranvaroReportFormService } from './franvaro-report-form.service';
import { FranvaroReportFormValidator } from './franvaro-report-form.validator';

@Component({
  selector: 'msfa-franvaro-report-form',
  templateUrl: './franvaro-report-form.component.html',
  styleUrls: ['./franvaro-report-form.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush,
})
export class FranvaroReportFormComponent {
  shouldValidate$ = new BehaviorSubject<boolean>(false);
  confirmDialogOpen$ = new BehaviorSubject<boolean>(false);
  franvaroFormGroup = new FormGroup(
    {
      reason: new FormControl(null),
      knownReasonComment: new FormControl(''),
      date: new FormControl(new Date()),
      wholeDay: new FormControl(true),
      startTime: new FormControl(null),
      endTime: new FormControl(null),
      expectedPresenceStartTime: new FormControl(null),
      expectedPresenceEndTime: new FormControl(null),
    },
    [FranvaroReportFormValidator.isFranvaroReportValid()]
  );

  submitError$ = new BehaviorSubject<CustomError>(null);
  submitLoading$ = new BehaviorSubject<boolean>(false);
  lastSubmittedFranvaroReport$ = new BehaviorSubject<Date>(null);
  currentGenomforandeReferens$: Observable<number> = this.activatedRoute.params.pipe(
    map(params => +params.genomforandeReferens)
  );
  avrop$: Observable<DeltagareAvrop> = this.currentGenomforandeReferens$.pipe(
    switchMap(genomforandeReferens => this.franvaroReportFormService.fetchAvropInformation$(genomforandeReferens)),
    shareReplay(1)
  );
  reasons$: Observable<FranvaroReason[]> = this.franvaroReportFormService.reasons$;
  allReasons$: Observable<FranvaroReason[]> = combineLatest([
    this.reasons$,
    this.franvaroReportFormService.otherKnownReasons$,
  ]).pipe(map(([reasons, otherKnownReasons]) => sortFranvaroReasons(reasons.concat(otherKnownReasons))));
  otherKnownReasons$: Observable<FranvaroReason[]> = this.franvaroReportFormService.otherKnownReasons$;

  wholeDayOrPartOfDayRadiobuttons: Radiobutton[] = [
    { label: 'Heldag', value: true },
    { label: 'Del av dag', value: false },
  ];

  constructor(private franvaroReportFormService: FranvaroReportFormService, private activatedRoute: ActivatedRoute) {}

  get showKnownReasonTextArea(): boolean {
    return this.reasonFormControl.value === ANNAN_ORSAK_ID;
  }

  get showTimePickers(): boolean {
    return !this.wholeDayFormControl.value;
  }

  get formErrors(): { [key: string]: string } {
    return this.franvaroFormGroup.errors || {};
  }

  get reasonFormControl(): FormControl {
    return this.franvaroFormGroup.get('reason') as FormControl;
  }

  get dateFormControl(): FormControl {
    return this.franvaroFormGroup.get('date') as FormControl;
  }

  get wholeDayFormControl(): FormControl {
    return this.franvaroFormGroup.get('wholeDay') as FormControl;
  }

  get startTimeFormControl(): FormControl {
    return this.franvaroFormGroup.get('startTime') as FormControl;
  }

  get endTimeFormControl(): FormControl {
    return this.franvaroFormGroup.get('endTime') as FormControl;
  }

  get expectedPresenceStartTimeFormControl(): FormControl {
    return this.franvaroFormGroup.get('expectedPresenceStartTime') as FormControl;
  }

  get expectedPresenceEndTimeFormControl(): FormControl {
    return this.franvaroFormGroup.get('expectedPresenceEndTime') as FormControl;
  }

  get knownReasonCommentFormControl(): FormControl {
    return this.franvaroFormGroup.get('knownReasonComment') as FormControl;
  }

  get dateFormValueAsDate(): Date {
    return new Date(this.dateFormControl.value);
  }

  get dayOrPartOfDayFromValue(): string {
    return this.wholeDayFormControl.value ? 'Heldag' : 'Del av dag';
  }

  private _isAfterStartDate(startDate: Date): boolean {
    return new Date() > startDate;
  }
  private _isBeforeLastPossibleReportDay(endDate: Date): boolean {
    // Reporting is allowed at latest 5 days past avrop end date.
    // Because it's workdays and not calendar days we temporarily set this to much more. This date should be fetched from API in the future
    const lastPossibleReportDay = addDays(endDate, 15);
    return lastPossibleReportDay > new Date();
  }

  isAllowedToReport(avrop: DeltagareAvrop): boolean {
    return this._isAfterStartDate(avrop.startDate) && this._isBeforeLastPossibleReportDay(avrop.endDate);
  }

  notAllowedToReportWarning(avrop: DeltagareAvrop): string {
    if (!this._isBeforeLastPossibleReportDay(avrop.endDate)) {
      return 'Det går inte att göra Avvikelserapport (frånvaro) eftersom tjänsten har avslutats.';
    }
    if (!this._isAfterStartDate(avrop.startDate)) {
      return 'Det går inte att göra Avvikelserapport (frånvaro) eftersom tjänsten inte har startat ännu.';
    }
  }

  getReasonNameFromValue(reasons: FranvaroReason[], value: string): string {
    return reasons.find(reason => reason.value.toString() === value)?.name;
  }

  maxDate(endDate: Date): Date {
    const now = new Date();
    return now < endDate ? now : endDate;
  }

  formControlIsInvalid(formControlNames: string[]): boolean {
    return (
      formControlNames.some(formControlName => this.formErrors[formControlName]) && this.shouldValidate$.getValue()
    );
  }

  openConfirmDialog(): void {
    this.shouldValidate$.next(true);
    if (this.franvaroFormGroup.invalid) {
      return;
    }

    this.confirmDialogOpen$.next(true);
  }

  cancelConfirmDialog(): void {
    this.confirmDialogOpen$.next(false);
    this.submitError$.next(null);
  }

  get isOtherKnownReason(): boolean {
    for (const orsakskod in OrsaksKoderKanda) {
      if (orsakskod === this.reasonFormControl.value) {
        return true;
      }
    }
  }

  submitAndCloseConfirmDialog(genomforandeReferens: number): void {
    this.submitLoading$.next(true);

    const {
      reason,
      date,
      wholeDay,
      startTime,
      endTime,
      knownReasonComment,
      expectedPresenceStartTime,
      expectedPresenceEndTime,
    } = this.franvaroFormGroup.value as Franvaro;

    const postRequest: FranvaroReportRequest = {
      genomforandeReferens: +genomforandeReferens,
      franvaro: {
        avvikelseOrsaksKod: setKnownReason(reason),
        datum: formatDate(date),
        heldag: wholeDay,
        startTid: this.showTimePickers ? startTime : '0:00', // BÄR doesn't accept empty string or null
        slutTid: this.showTimePickers ? endTime : '23:59', // BÄR doesn't accept empty string or null
        alternativForKandaOrsaker: this.isOtherKnownReason
          ? {
              typ: reason,
              motivering: this.showKnownReasonTextArea ? knownReasonComment : '',
            }
          : null,
        forvantadNarvaro: {
          startTid: expectedPresenceStartTime,
          slutTid: expectedPresenceEndTime,
        },
      },
    };

    this.franvaroReportFormService
      .postFranvaroReport$(postRequest)
      .pipe(take(1))
      .subscribe({
        next: () => {
          this.lastSubmittedFranvaroReport$.next(new Date());
          this.submitLoading$.next(false);
          this.confirmDialogOpen$.next(false);
        },
        error: (customError: CustomError) => {
          this.submitError$.next({ ...customError, message: customError.error.message });
          this.submitLoading$.next(false);
          throw { ...customError, avoidToast: true };
        },
      });
  }
}
