using System.Reactive.Linq;


namespace InnovEnergy.Lib.Utils;

public static class ObservableExceptionHandling
{
    public static IObservable<Try<R>> Try<T, R>(this IObservable<T> src, Func<T, Task<R>> map)
    {
        return src.SelectMany(t => ExceptionHandling.Try(() => map(t)));
    }


    public static IObservable<Try<R>> Try<T, R>(this IObservable<T> src, Func<T, R> map)
    {
        return src.Select(t => ExceptionHandling.Try(() => map(t)));
    }

    public static IObservable<Try<R>> ThenTry<T, R>(this IObservable<Try<T>> src, Func<T, R> map)
    {
        return src.Select(t => ExceptionHandling.ThenTry(t, map));
    }

    // public static IObservable<T> Catch<T>(this IObservable<Try<T>> src, Func<Exception, T> onError)
    // {
    //     return src.Select(t => ExceptionHandling.Catch(t, onError));
    // }


    // TODO: onError should be a func returning T
    // public static IObservable<T> Catch<T>(this IObservable<Try<T>> src, Action<Exception> onError)
    // {
    //     return src
    //           .OnErrorDo(onError)
    //           .IgnoreErrors();
    // }

    public static IObservable<T> IgnoreErrors<T>(this IObservable<Try<T>> src)
    {
        return src
            .Where(t => !(t.Wrapped is Exception))
            .Select(t => (T) t.Wrapped);
    }

    public static IObservable<Try<T>> OnErrorDo<T>(this IObservable<Try<T>> src, Action<Exception> onError)
    {
        return src.Do(t=>
        {
            if (t.Wrapped is Exception e)
                onError(e);
        });
    }

    public static IObservable<Try<T>> Match<T>(this IObservable<Try<T>> src, Action<T> onValue, Action<Exception> onError)
    {
        return src.Do(t=>
        {
            if (t.Wrapped is Exception e)
                onError(e);
            else
                onValue((T) t.Wrapped);
        });
    }



    // public static IObservable<CanFail<R>> Try<T,R>(this IObservable<T> observable, Func<T,R> map)
    // {
    //     return observable
    //           .Select(e => CanFail.Try(() => map(e)));
    // }
    //
    // public static IObservable<CanFail<R>> Try<T,R>(this IObservable<CanFail<T>> observable, Func<T,R> map)
    // {
    //     return observable
    //           .Select(t => t.Select(map));
    // }
    //
    //
    // public static IObservable<CanFail<R>> TryAsync<T, R>(this IObservable<T> observable, Func<T, Task<R>> map)
    // {
    //     return observable
    //           .SelectMany(e => CanFail.TryAsync(() => map(e)));
    // }
    //
    //
    // public static IObservable<CanFail<R>> TryAsync<T,R>(this IObservable<CanFail<T>> observable,
    //                                                                  Func<T,Task<R>> map)
    // {
    //     return observable
    //           .SelectMany(t => CanFail.SelectMany(t, map));
    // }
    //
    //
    // public static IObservable<CanFail<T>> OnErrorDo<T>(this IObservable<CanFail<T>> observable, Action<Exception> onError)
    // {
    //     return observable
    //           .Select(t =>
    //           {
    //               if (t is Failure<T> f)
    //                   onError(f.Error);
    //
    //               return t;
    //           });
    // }

    // public static IObservable<R> TryCatch<T,R,E>(this IObservable<T> observable,
    //                                                        Func<T,R> map,
    //                                                        Func<E,R> exceptionHandler)
    //     where E : Exception
    // {
    //     R TryMap(T i)
    //     {
    //         try
    //         {
    //             return map(i);
    //         }
    //         catch (E e)
    //         {
    //             return exceptionHandler(e);
    //         }
    //     }
    //
    //     return observable.Select(TryMap);
    // }

    // public static IObservable<CanFail<R>> Try<T,R,E>(this IObservable<T> observable,
    //     Func<T,R> map,
    //     Func<E, CanFail<R>> exceptionHandler)
    //     where E : Exception
    // {
    //     CanFail<R> TryMap(T i)
    //     {
    //         try
    //         {
    //             return CanFail.Success(map(i));
    //         }
    //         catch (E e)
    //         {
    //             return exceptionHandler(e);
    //         }
    //     }
    //
    //     return observable.Select(TryMap);
    // }


    // public static IObservable<R> TryAsync<T,R>(this IObservable<T> observable,
    //                                                Func<T,Task<R>> map,
    //                                              Action<Exception> onError)
    // {
    //     async Task<Notification<R>> TryMap(T i)
    //     {
    //         try
    //         {
    //             return Notification.CreateOnNext(await map(i));
    //         }
    //         catch (Exception e)
    //         {
    //             onError(e);
    //             return Notification.CreateOnError<R>(e);
    //         }
    //     }
    //
    //     return observable
    //           .SelectMany(TryMap)
    //           .Where(n => n.Kind != NotificationKind.OnError)
    //           .Dematerialize();
    // }



    // public static IObservable<R> Try<T,R>(this IObservable<T> observable, Func<T,R> map)
    // {
    //     Notification<R> TryMap(T i)
    //     {
    //         try
    //         {
    //             return Notification.CreateOnNext(map(i));
    //         }
    //         catch (Exception e)
    //         {
    //             return Notification.CreateOnError<R>(e);
    //         }
    //     }
    //
    //     return observable
    //           .Select(TryMap)
    //           .Where(n => n.Kind != NotificationKind.OnError)
    //           .Dematerialize();
    // }
    //
    // public static IObservable<R> Try<T,R>(this IObservable<T> observable,
    //                                                 Func<T,R> map,
    //                                         Action<Exception> onError)
    // {
    //     Notification<R> TryMap(T i)
    //     {
    //         try
    //         {
    //             return Notification.CreateOnNext(map(i));
    //         }
    //         catch (Exception e)
    //         {
    //             onError(e);
    //             return Notification.CreateOnError<R>(e);
    //         }
    //     }
    //
    //     return observable
    //           .Select(TryMap)
    //           .Where(n => n.Kind != NotificationKind.OnError)
    //           .Dematerialize();
    // }
    //
    // public static IObservable<R> Try<T,R>(this IObservable<T> observable,
    //                                                 Func<T,R> map,
    //                                   Func<Exception,Boolean> ignoreException)
    // {
    //     Notification<R> TryMap(T i)
    //     {
    //         try
    //         {
    //             return Notification.CreateOnNext(map(i));
    //         }
    //         catch (Exception e)
    //         {
    //             if (ignoreException(e))
    //                 return Notification.CreateOnError<R>(e);
    //
    //             throw;
    //
    //             // throw ex resets the stack trace (so your errors would appear to originate from HandleException)
    //             // throw doesn't - the original offender would be preserved.
    //         }
    //     }
    //
    //     return observable
    //           .Select(TryMap)
    //           .Where(n => n.Kind != NotificationKind.OnError)
    //           .Dematerialize();
    // }


}