namespace Baya.Application.Models.Common;

/// <summary>
/// Marker Interface To Mark Operation Result Response In Validation Pipeline
/// </summary>
public interface IOperationResult
{
    bool IsSuccess { get; set; }
    List<KeyValuePair<string,string>> ErrorMessages { get; set; }
    bool IsException { get; set; }
    bool IsNotFound { get; set; }
}

public class OperationResult<TResult> : IOperationResult
{
    public TResult Result { get;  set; }

    public bool IsSuccess { get; set; }
    public List<KeyValuePair<string,string>> ErrorMessages { get; set; } = new();
    public bool IsException { get; set; }
    public bool IsNotFound { get; set; }

    /// <summary>Maps to HTTP 401 — e.g. a rejected/reused refresh token (backend-phase-2).</summary>
    public bool IsUnauthorized { get; set; }

    /// <summary>Maps to HTTP 403 — e.g. self-assigning an internal admin role (backend-phase-2).</summary>
    public bool IsForbidden { get; set; }

    /// <summary>Maps to HTTP 409 — a uniqueness/state conflict, e.g. a duplicate nurse service area
    /// (backend-phase-4). Distinct from a validation 400 so callers can react to the collision.</summary>
    public bool IsConflict { get; set; }

    /// <summary>Optional stable machine-readable error code (e.g. <c>otp_locked</c>) surfaced on the failure
    /// envelope so the client can branch on the exact condition. Null for ordinary failures.</summary>
    public string ErrorCode { get; set; }

    /// <summary>Optional structured payload that accompanies <see cref="ErrorCode"/> (e.g. a lockout's
    /// <c>retryAfterSeconds</c>). Serialized under the envelope's <c>data</c> when present.</summary>
    public object ErrorData { get; set; }

    public static OperationResult<TResult> SuccessResult(TResult result)
    {
        return new OperationResult<TResult> { Result = result, IsSuccess = true };
    }

    public static OperationResult<TResult> FailureResult(string propertyName, string message, TResult result = default)
    {
        var operationResult = new OperationResult<TResult> { Result = result, IsSuccess = false };

        operationResult.ErrorMessages.Add(new(propertyName,message));

        return operationResult;
    }    
    public static OperationResult<TResult> FailureResult(string message, TResult result = default)
    {
        var operationResult = new OperationResult<TResult> { Result = result, IsSuccess = false };

        operationResult.ErrorMessages.Add(new("GeneralError",message));

        return operationResult;
    }
    public static OperationResult<TResult> NotFoundResult(string message)
    {
        var operationResult = new OperationResult<TResult> { IsSuccess = false, IsNotFound = true };

        operationResult.ErrorMessages.Add(new("GeneralError", message));

        return operationResult;
    }

    public static OperationResult<TResult> UnauthorizedResult(string message)
    {
        var operationResult = new OperationResult<TResult> { IsSuccess = false, IsUnauthorized = true };

        operationResult.ErrorMessages.Add(new("GeneralError", message));

        return operationResult;
    }

    public static OperationResult<TResult> ForbiddenResult(string message)
    {
        var operationResult = new OperationResult<TResult> { IsSuccess = false, IsForbidden = true };

        operationResult.ErrorMessages.Add(new("GeneralError", message));

        return operationResult;
    }

    public static OperationResult<TResult> ConflictResult(string message)
    {
        var operationResult = new OperationResult<TResult> { IsSuccess = false, IsConflict = true };

        operationResult.ErrorMessages.Add(new("GeneralError", message));

        return operationResult;
    }

    /// <summary>A validation-style failure (HTTP 400) that also carries a stable machine <paramref name="code"/>
    /// (and optional structured <paramref name="data"/>) on the envelope — for a condition the client must
    /// distinguish, e.g. an OTP lockout.</summary>
    public static OperationResult<TResult> CodedFailureResult(string code, string message, object data = null)
    {
        var operationResult = new OperationResult<TResult> { IsSuccess = false, ErrorCode = code, ErrorData = data };

        operationResult.ErrorMessages.Add(new("GeneralError", message));

        return operationResult;
    }

    public void AddError(string propertyName, string message)
    {
        IsSuccess = false;
        
        ErrorMessages.Add(new(propertyName,message));
    }
    
    public string GetErrorMessage()
    => string.Join(Environment.NewLine, ErrorMessages.Select(x => $"{x.Key}: {x.Value}"));
}