import { catchError, mergeMap } from 'rxjs/operators';
import { OperatorFunction, throwError, pipe } from 'rxjs';
import { ResponseArgsError } from '../errors';
import { HttpErrorResponse } from '@angular/common/http';
import { isResponseArgs } from '../helpers';

/**
 *
 * Operator that catches errors from an observable stream and transforms them into
 *
 * `ResponseArgsError` instances when applicable.
 * This operator is useful for handling HTTP errors that return a structured
 * response conforming to the `ResponseArgs` interface.
 *
 * If the error is already a `ResponseArgsError`, it is re-thrown as is.
 * If the error is an `HttpErrorResponse` with a valid `ResponseArgs` payload,
 * it creates and throws a new `ResponseArgsError`.
 * For all other error types, it re-throws the original error.
 *
 */
export const catchResponseArgsErrorPipe = <T>(): OperatorFunction<T, T> =>
  pipe(
    catchError((err: unknown) => {
      if (err instanceof ResponseArgsError) {
        return throwError(() => err);
      }

      if (err instanceof HttpErrorResponse) {
        const payload = err.error;
        if (isResponseArgs(payload)) {
          return throwError(() => new ResponseArgsError(payload));
        }
      }
      return throwError(() => err);
    }),
    mergeMap((response) => {
      if (isResponseArgs(response)) {
        // Treat as error if error flag is true OR if invalidProperties has entries
        const hasInvalidProps =
          response.invalidProperties &&
          Object.keys(response.invalidProperties).length > 0;

        if (response.error === true || hasInvalidProps) {
          return throwError(() => new ResponseArgsError(response));
        }
      }

      return [response];
    }),
  );
