using System.Net;
using System.Runtime.Serialization;
using EasyDevCore.Common;
namespace EasyDevCore.Remote
{
    /// <summary>
    /// Enum ResultMode
    /// </summary>
    public enum ResultMode
    {
        /// <summary>
        /// The success
        /// </summary>
        Success = 0,
        /// <summary>
        /// The error
        /// </summary>
        Error = 1,
        /// <summary>
        /// The information
        /// </summary>
        Info = 2,
        /// <summary>
        /// The warning
        /// </summary>
        Warning = 3,
        /// <summary>
        /// The Confirmation
        /// </summary>
        Confirmation = 4
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData">The type of the data.</typeparam>
    public interface IValueResult<TData>
    {
        /// <summary>
        /// Gets or sets the data.
        /// </summary>
        /// <value>
        /// The data.
        /// </value>
        TData Data { get; set; }
        /// <summary>
        /// Gets a value indicating whether this instance is error.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is error; otherwise, <c>false</c>.
        /// </value>
        bool IsError { get; }
        /// <summary>
        /// Gets a value indicating whether this instance is success.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is success; otherwise, <c>false</c>.
        /// </value>
        bool IsSuccess { get; }
        /// <summary>
        /// Gets or sets the message.
        /// </summary>
        /// <value>
        /// The message.
        /// </value>
        string Message { get; set; }
        /// <summary>
        /// Gets or sets the result code.
        /// </summary>
        /// <value>
        /// The result code.
        /// </value>
        string ResultCode { get; set; }
        /// <summary>
        /// Gets or sets the result status.
        /// </summary>
        /// <value>
        /// The result status.
        /// </value>
        ResultMode ResultStatus { get; set; }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData">The type of the data.</typeparam>
    /// <seealso cref="IValueResult&lt;TData&gt;" />
    public abstract class ValueResultBase<TData> : IValueResult<TData>
    {
        /// <summary>
        /// Gets or sets the data.
        /// </summary>
        /// <value>
        /// The data.
        /// </value>
        public TData Data { get; set; }
        /// <summary>
        /// Gets or sets the result status.
        /// </summary>
        /// <value>
        /// The result status.
        /// </value>
        public ResultMode ResultStatus { get; set; } = ResultMode.Success;
        /// <summary>
        /// Gets or sets the result code.
        /// </summary>
        /// <value>
        /// The result code.
        /// </value>
        public string ResultCode { get; set; } = "";
        /// <summary>
        /// Gets or sets the message.
        /// </summary>
        /// <value>
        /// The message.
        /// </value>
        public string Message { get; set; } = "";
        /// <summary>
        /// Gets a value indicating whether this instance is error.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is error; otherwise, <c>false</c>.
        /// </value>
        public virtual bool IsError => ResultStatus == ResultMode.Error;

        /// <summary>
        /// Gets a value indicating whether this instance is success.
        /// </summary>
        /// <value>
        ///   <c>true</c> if this instance is success; otherwise, <c>false</c>.
        /// </value>
        public bool IsSuccess => !IsError;
        /// <summary>
        /// Maps the specified convert.
        /// </summary>
        /// <typeparam name="TMapData">The type of the map data.</typeparam>
        /// <param name="convert">The convert.</param>
        /// <returns></returns>
        public abstract ValueResultBase<TMapData> Map<TMapData>(Func<TData, TMapData> convert);
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData">The type of the value.</typeparam>
    public class Result<TData> : ValueResultBase<TData>
    {
        /// <summary>
        /// Gets or sets the error.
        /// </summary>
        /// <value>
        /// The error.
        /// </value>
        [IgnoreDataMember]
        public Exception Error { get; set; }
        /// <summary>
        /// Gets a value indicating whether this instance is error.
        /// </summary>
        /// <value>
        /// <c>true</c> if this instance is error; otherwise, <c>false</c>.
        /// </value>
        public override bool IsError => ResultStatus == ResultMode.Error || Error != null;
        /// <summary>
        /// Gets or sets the HTTP result status.
        /// </summary>
        /// <value>
        /// The HTTP result status.
        /// </value>
        [IgnoreDataMember]
        public HttpStatusCode HttpResultStatus { get; set; } = HttpStatusCode.OK;

        /// <summary>
        /// Initializes a new instance of the <see cref="Result{TData}"/> class.
        /// </summary>
        internal Result()
        {

        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Result{TData}"/> class.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="resultStatus">The result status.</param>
        /// <param name="resultCode">The result code.</param>
        /// <param name="message">The message.</param>
        public Result(TData data, ResultMode resultStatus = ResultMode.Success, string resultCode = "", string message = "")
        {
            Data = data;
            ResultStatus = resultStatus;
            ResultCode = resultCode;
            Message = message;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Result{TData}" /> class.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="error">The exception.</param>
        public Result(TData data, Exception error)
        {
            ResultCode = error.HResult.ToString();
            ResultStatus = ResultMode.Error;
            Message = error.Message;
            HttpResultStatus = HttpStatusCode.BadRequest;
            Error = error;
        }

        /// <summary>
        /// Maps the specified convert.
        /// </summary>
        /// <typeparam name="TMapData">The type of the map data.</typeparam>
        /// <param name="convert">The convert.</param>
        /// <returns></returns>
        public override Result<TMapData> Map<TMapData>(Func<TData, TMapData> convert)
        {
            return new Result<TMapData>
            {
                Data = convert(Data!),
                Message = Message,
                ResultCode = ResultCode,
                ResultStatus = ResultStatus,
                Error = Error,
                HttpResultStatus = HttpResultStatus
            };
        }
    }

    /// <summary>
    /// 
    /// </summary>
    /// <typeparam name="TData">The type of the value.</typeparam>
    /// <seealso cref="Result&lt;TValue&gt;" />
    public class UpdateResult<TData> : Result<TData>
    {
        /// <summary>
        /// Gets or sets the updated date.
        /// </summary>
        /// <value>
        /// The updated date.
        /// </value>
        public object UpdatedDate { get; set; }
        /// <summary>
        /// Gets or sets the updated by.
        /// </summary>
        /// <value>
        /// The updated by.
        /// </value>
        public object UpdatedBy { get; set; }

        /// <summary>
        /// Initializes a new instance of the <see cref="UpdateResult{TData}"/> class.
        /// </summary>
        internal UpdateResult()
        {

        }
        /// <summary>
        /// Initializes a new instance of the <see cref="UpdateResult{TValue}"/> class.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="updatedDate">The updated date.</param>
        /// <param name="updatedBy">The updated by.</param>
        /// <param name="resultStatus">The result status.</param>
        /// <param name="resultCode">The result code.</param>
        /// <param name="message">The message.</param>
        public UpdateResult(TData data, object updatedDate, object updatedBy, ResultMode resultStatus = ResultMode.Success, string resultCode = "", string message = "") : base(data, resultStatus, resultCode, message)
        {
            UpdatedDate = updatedDate;
            UpdatedBy = updatedBy;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Result{TValue}"/> class.
        /// </summary>
        /// <param name="data">The data.</param>
        /// <param name="error">The exception.</param>
        public UpdateResult(TData data, Exception error) : base(data, error)
        {
        }

        /// <summary>
        /// Maps the specified convert.
        /// </summary>
        /// <typeparam name="TMapData">The type of the map data.</typeparam>
        /// <param name="convert">The convert.</param>
        /// <returns></returns>
        public override UpdateResult<TMapData> Map<TMapData>(Func<TData, TMapData> convert)
        {
            return new UpdateResult<TMapData>
            {
                Data = convert(Data!),
                Message = Message,
                ResultCode = ResultCode,
                ResultStatus = ResultStatus,
                UpdatedBy = UpdatedBy,
                UpdatedDate = UpdatedDate,
                Error = Error,
                HttpResultStatus = HttpResultStatus
            };
        }
    }
}
