import { HttpHeaders } from '@ngify/http';
import { inject, Injectable } from 'injection-js';
import { type Observable, throwError } from 'rxjs';
import { catchError, map, retry, switchMap } from 'rxjs/operators';
import { DataService } from '../../api/data.service';
import type { AuthOptions } from '../../auth-options';
import type { OpenIdConfiguration } from '../../config/openid-configuration';
import { LoggerService } from '../../logging/logger.service';
import { StoragePersistenceService } from '../../storage/storage-persistence.service';
import { UrlService } from '../../utils/url/url.service';
import type { ParResponse } from './par-response';

@Injectable()
export class ParService {
  private readonly loggerService = inject(LoggerService);

  private readonly urlService = inject(UrlService);

  private readonly dataService = inject(DataService);

  private readonly storagePersistenceService = inject(
    StoragePersistenceService
  );

  postParRequest(
    configuration: OpenIdConfiguration,
    authOptions?: AuthOptions
  ): Observable<ParResponse> {
    let headers: HttpHeaders = new HttpHeaders();

    headers = headers.set('Content-Type', 'application/x-www-form-urlencoded');

    const authWellKnownEndpoints = this.storagePersistenceService.read(
      'authWellKnownEndPoints',
      configuration
    );

    if (!authWellKnownEndpoints) {
      return throwError(
        () =>
          new Error(
            'Could not read PAR endpoint because authWellKnownEndPoints are not given'
          )
      );
    }

    const parEndpoint = authWellKnownEndpoints.parEndpoint;

    if (!parEndpoint) {
      return throwError(
        () =>
          new Error('Could not read PAR endpoint from authWellKnownEndpoints')
      );
    }

    return this.urlService
      .createBodyForParCodeFlowRequest(configuration, authOptions)
      .pipe(
        switchMap((data) => {
          return this.dataService
            .post(parEndpoint, data, configuration, headers)
            .pipe(
              retry(2),
              map((response: any) => {
                this.loggerService.logDebug(
                  configuration,
                  'par response: ',
                  response
                );

                return {
                  expiresIn: response.expires_in,
                  requestUri: response.request_uri,
                };
              }),
              catchError((error) => {
                const errorMessage = 'There was an error on ParService postParRequest';

                this.loggerService.logError(configuration, errorMessage, error);

                return throwError(() => new Error(errorMessage));
              })
            );
        })
      );
  }
}
