// [RequiredIf("SomeOtherProperty")] -- required if the other property is true
// [RequiredIf("SomeIntProperty", OtherPropertyValue = 42)] -- required if the other property is 42

public class RequiredIfAttribute : ValidationAttribute, IClientValidatable
{
    public string OtherPropertyName { get; }
    public object OtherPropertyValue { get; set; }

    public RequiredIfAttribute(string otherPropertyName)
    {
        OtherPropertyName = otherPropertyName;
    }

    public override bool RequiresValidationContext => true;

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        var prop = validationContext.ObjectType.GetProperty(OtherPropertyName);
        if (prop == null)
            throw new InvalidOperationException($"Could not find property {OtherPropertyName} on type {validationContext.ObjectType}.");

        var otherValue = prop.GetValue(validationContext.ObjectInstance);
        bool isRequired;
        if (otherValue is true)
            isRequired = true;
        else
        {
            isRequired = otherValue?.Equals(OtherPropertyValue) ?? false;
        }

        if (isRequired)
        {
            if (value == null || (value is string s && s.Trim() == ""))
            {
                return new ValidationResult(FormatErrorMessage(validationContext.DisplayName));
            }
        }

        return ValidationResult.Success;
    }

    public override string FormatErrorMessage(string name)
    {
        return string.Format(ErrorMessageString, name, OtherPropertyName);
    }

    // adds a data-val-requiredif attribute data-val-requireif-other="OtherPropertyName" data-val-requiredif-value="42"
    public IEnumerable<ModelClientValidationRule> GetClientValidationRules(ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule()
        {
            ErrorMessage = FormatErrorMessage(metadata.DisplayName),
            ValidationType = "requiredif",
            ValidationParameters =
            {
                {
                    "other", OtherPropertyName
                }
            }
        };

        if (OtherPropertyValue != null)
        {
            rule.ValidationParameters["value"] = OtherPropertyValue.ToString();
        }

        yield return rule;
    }
}