using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Text;

namespace InABox.Core
{
    public static class Result
    {
        public static OkResult Ok()
        {
            return new OkResult();
        }
        public static ErrorResult<E> Error<E>(E error)
        {
            return new ErrorResult<E>(error);
        }

        public static OkResult<T> Ok<T>(T value)
        {
            return new OkResult<T>(value);
        }

        public static Result<E> Flatten<E>(this Result<Result<E>, E> result)
        {
            if(result.Get(out var inner, out var error))
            {
                return inner;
            }
            else
            {
                return Result.Error(error);
            }
        }

        public static Result<T, E> Flatten<T, E>(this Result<Result<T, E>, E> result)
        {
            if(result.Get(out var inner, out var error))
            {
                return inner;
            }
            else
            {
                return Result.Error(error);
            }
        }
    }

    public class OkResult
    {
    }

    public class OkResult<T>
    {
        public T Value;

        public OkResult(T value)
        {
            Value = value;
        }
    }

    public class ErrorResult<E>
    {
        public E Error;

        public ErrorResult(E error)
        {
            Error = error;
        }
    }

    public class Result<E>
    {
        public enum Status
        {
            Ok,
            Error
        }

        private E _error;
        private Status _status;

        public Result()
        {
            _error = default;
            _status = Status.Ok;
        }
        public Result(E error)
        {
            _error = error;
            _status = Status.Error;
        }

        public static implicit operator Result<E>(OkResult r)
        {
            return new Result<E>();
        }
        public static implicit operator Result<E>(ErrorResult<E> r)
        {
            return new Result<E>(r.Error);
        }

        public bool Get([NotNullWhen(false)][MaybeNull] out E error)
        {
            error = _error;
            return _status == Status.Ok;
        }

        public Result<U, E> MapOk<U>(Func<U> mapFunc)
        {
            if(_status == Status.Ok)
            {
                return Result.Ok(mapFunc());
            }
            else
            {
                return Result.Error(_error);
            }
        }

        public Result<E> MapOk(Action mapFunc)
        {
            if(_status == Status.Ok)
            {
                mapFunc();
                return Result.Ok();
            }
            else
            {
                return Result.Error(_error);
            }
        }
    }

    public class Result<T, E>
    {
        public enum Status
        {
            Ok,
            Error
        }

        private T _value;
        private E _error;
        private Status _status;

        public bool IsOK => _status == Status.Ok;

        public Result(T value)
        {
            _value = value;
            _error = default;
            _status = Status.Ok;
        }
        public Result(E error)
        {
            _value = default;
            _error = error;
            _status = Status.Error;
        }

        public static implicit operator Result<T, E>(OkResult<T> r)
        {
            return new Result<T, E>(r.Value);
        }
        public static implicit operator Result<T, E>(ErrorResult<E> r)
        {
            return new Result<T, E>(r.Error);
        }

        public bool Get([NotNullWhen(true)][MaybeNull] out T value, [NotNullWhen(false)][MaybeNull] out E error)
        {
            value = _value;
            error = _error;
            return _status == Status.Ok;
        }
        public bool GetOk([NotNullWhen(true)][MaybeNull] out T value)
        {
            value = _value;
            return _status == Status.Ok;
        }

        public Result<U, E> MapOk<U>(Func<T, U> mapFunc)
        {
            if(_status == Status.Ok)
            {
                return Result.Ok(mapFunc(_value));
            }
            else
            {
                return Result.Error(_error);
            }
        }
    }
}
