package llc.arma.ble.domain

class Result<T, E> internal constructor(
    val value: Any?
) {

    class Failure<E>(
        val error: E
    )

    companion object {

        fun <T, E> success(value: T): Result<T, E> {
            return Result(value)
        }

        fun <T, E> failure(error: E): Result<T, E> {
            return Result(Failure(error))
        }

    }

    val isSuccess: Boolean get() = value !is Failure<*>

    val isFailure: Boolean get() = value is Failure<*>

    inline fun <R> fold(
        onSuccess: (value: T) -> R,
        onFailure: (error: E) -> R
    ): R {
        return when (value) {
            is Failure<*> -> onFailure(value.error as E)
            else -> onSuccess(value as T)
        }
    }

    inline fun <R> map(
        mapper: (value: T) -> R,
    ): Result<R, E> {
        return when (value) {
            is Failure<*> -> failure(value.error as E)
            else -> success(mapper.invoke(value as T))
        }
    }

    inline fun onSuccess(
        let: (result: T) -> Unit
    ){
        if(isSuccess){
            let.invoke(value as T)
        }
    }

    inline fun onFailure(
        let: (error: E) -> Unit
    ){
        if(value is Failure<*>){
            let.invoke(value.error as E)
        }
    }

    fun getOrNull(): T? {
        return when (value) {
            is Failure<*> -> null
            else -> value as T
        }
    }

    fun getErrorOrNull(): E? {
        return when (value) {
            is Failure<*> -> value.error as E
            else -> null
        }
    }

}