import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { environment } from '@msfa-environment';
import { DeltagareAvropResponse } from '@msfa-models/api/avrop.response.model';
import { ContactInformationResponse } from '@msfa-models/api/contact-information.response.model';
import { DeltagareCompactApiResponse } from '@msfa-models/api/deltagare.response.model';
import { DisabilityResponse } from '@msfa-models/api/disability.response.model';
import { DriversLicenseResponse } from '@msfa-models/api/drivers-license.response.model';
import { EducationsResponse } from '@msfa-models/api/educations.response.model';
import { HighestEducationResponse } from '@msfa-models/api/highest-education.response.model';
import { DeltagareParams, PaginationParams, Params } from '@msfa-models/api/params.model';
import { ReportsDataResponse } from '@msfa-models/api/report.response.model';
import { TranslatorResponse } from '@msfa-models/api/translator.response.model';
import { WorkExperiencesResponse } from '@msfa-models/api/work-experiences.response.model';
import { WorkLanguagesResponse } from '@msfa-models/api/work-languages.response.model';
import { DeltagareAvrop, mapResponseToDeltagareAvrop } from '@msfa-models/avrop.model';
import { ContactInformation, mapResponseToContactInformation } from '@msfa-models/contact-information.model';
import { DeltagareCompactData, mapResponseToDeltagareCompact } from '@msfa-models/deltagare.model';
import { Disability, mapResponseToDisability } from '@msfa-models/disability.model';
import { DriversLicense, mapResponseToDriversLicense } from '@msfa-models/drivers-license.model';
import { Education, mapResponseToEducation } from '@msfa-models/education.model';
import { CustomError, errorToCustomError } from '@msfa-models/error/custom-error';
import { HighestEducation, mapResponseToHighestEducation } from '@msfa-models/highest-education.model';
import { mapResponseToReport, ReportsData } from '@msfa-models/report.model';
import { mapResponseToWorkExperience, WorkExperience } from '@msfa-models/work-experience.model';
import { sortFromToDates } from '@msfa-utils/sort.util';
import { BehaviorSubject, Observable, of } from 'rxjs';
import { catchError, map } from 'rxjs/operators';

@Injectable({
  providedIn: 'root',
})
export class DeltagareApiService {
  private _apiBaseUrl = `${environment.api.url}/deltagare`;
  private _apiReportUrl = `${environment.api.url}/handlingar`;
  private _deltagareLoading$ = new BehaviorSubject<boolean>(false);
  public deltagareLoading$: Observable<boolean> = this._deltagareLoading$.asObservable();
  private _showUnauthorizedError$ = new BehaviorSubject<boolean>(false);
  public showUnauthorizedError$: Observable<boolean> = this._showUnauthorizedError$.asObservable();

  constructor(private httpClient: HttpClient) {}

  public fetchAllDeltagare$(deltagareParams: DeltagareParams): Observable<DeltagareCompactData> {
    const { sort, order, limit, page, onlyMyDeltagare } = deltagareParams;
    const params: Params = {
      sort,
      order,
      limit: limit.toString(),
      page: page.toString(),
    };

    if (onlyMyDeltagare) {
      params.onlyMyDeltagare = onlyMyDeltagare.toString();
    }

    this._deltagareLoading$.next(true);

    return this.httpClient
      .get<DeltagareCompactApiResponse | null>(this._apiBaseUrl, {
        params,
      })
      .pipe(
        map(({ data, meta }) => {
          this._deltagareLoading$.next(false);
          return { data: data.map(deltagare => mapResponseToDeltagareCompact(deltagare)), meta };
        }),
        catchError((error: Error & { status: number }) => {
          if (error.status === 403) {
            this._showUnauthorizedError$.next(true);
            return of(null as null);
          } else {
            throw new CustomError(
              errorToCustomError({ ...error, message: `Kunde inte hämta deltagare.\n\n${error.message}` })
            );
          }
        })
      );
  }

  public fetchReports$(genomforandeReferens: number, paginationParams: PaginationParams): Observable<ReportsData> {
    const { page, limit } = paginationParams;
    const params: { [param: string]: string | string[] } = {
      genomforandeReferens: genomforandeReferens.toString(),
      page: page.toString(),
      limit: limit.toString(),
    };

    return this.httpClient
      .get<ReportsDataResponse>(`${this._apiReportUrl}`, { params })
      .pipe(
        map(({ data, meta }) => ({ data: data.map(report => mapResponseToReport(report)), meta })),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta rapporter.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchContactInformation$(genomforandeReferens: number): Observable<ContactInformation> {
    return this.httpClient
      .get<{ data: ContactInformationResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/contact`)
      .pipe(
        map(({ data }) => mapResponseToContactInformation(data)),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta kontaktinformation.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchDriversLicense$(genomforandeReferens: number): Observable<DriversLicense> {
    return this.httpClient
      .get<{ data: DriversLicenseResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/driverlicense`)
      .pipe(
        map(({ data }) => mapResponseToDriversLicense(data)),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta körkortsinformation.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchHighestEducation$(genomforandeReferens: number): Observable<HighestEducation> {
    return this.httpClient
      .get<{ data: HighestEducationResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/educationlevels/highest`)
      .pipe(
        map(({ data }) => mapResponseToHighestEducation(data)),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta högsta utbildning.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchEducations$(genomforandeReferens: number): Observable<Education[]> {
    return this.httpClient
      .get<{ data: EducationsResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/educations`)
      .pipe(
        map(({ data }) =>
          data.utbildningar
            ? data.utbildningar.sort((a, b) =>
                sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
              )
            : []
        ),
        map(educations => educations.map(utbildning => mapResponseToEducation(utbildning))),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta utbildningar.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchTranslator$(genomforandeReferens: number): Observable<string> {
    return this.httpClient
      .get<{ data: TranslatorResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/translator`)
      .pipe(
        map(({ data }) => data.sprak?.beskrivning || null),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta tolkinformation.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchWorkLanguages$(genomforandeReferens: number): Observable<string[]> {
    return this.httpClient
      .get<{ data: WorkLanguagesResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/languages`)
      .pipe(
        map(({ data }) => data?.sprak?.map(sprak => sprak.beskrivning) || []),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({
              ...error,
              message: `Kunde inte hämta språk som kan användas på jobbet.\n\n${error.message}`,
            })
          );
        })
      );
  }

  public fetchDisabilities$(genomforandeReferens: number): Observable<Disability[]> {
    return this.httpClient
      .get<{ data: DisabilityResponse[] }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/disabilities`)
      .pipe(
        map(({ data }) => data?.map(funktionsnedsattning => mapResponseToDisability(funktionsnedsattning)) || []),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta funktionsnedsättningar.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchWorkExperiences$(genomforandeReferens: number): Observable<WorkExperience[]> {
    return this.httpClient
      .get<{ data: WorkExperiencesResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/work/experiences`)
      .pipe(
        map(
          ({ data }) =>
            data?.arbetslivserfarenheter?.sort((a, b) =>
              sortFromToDates({ from: a.period_from, to: a.period_tom }, { from: b.period_from, to: b.period_tom })
            ) || []
        ),
        map(workExperiences => workExperiences.map(erfarenhet => mapResponseToWorkExperience(erfarenhet))),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta arbetslivserfarenheter.\n\n${error.message}` })
          );
        })
      );
  }

  public fetchAvropInformation$(genomforandeReferens: number): Observable<DeltagareAvrop> {
    return this.httpClient
      .get<{ data: DeltagareAvropResponse }>(`${this._apiBaseUrl}/${genomforandeReferens}/avrop`)
      .pipe(
        map(({ data }) => mapResponseToDeltagareAvrop(data)),
        catchError((error: Error) => {
          throw new CustomError(
            errorToCustomError({ ...error, message: `Kunde inte hämta avropsinformation.\n\n${error.message}` })
          );
        })
      );
  }
}
