///
/// Copyright (c) 2016 Dropbox, Inc. All rights reserved.
///

#import "DBStoneValidators.h"

@implementation DBStoneValidators

+ (void (^)(NSString *))stringValidator:(NSNumber *)minLength
                              maxLength:(NSNumber *)maxLength
                                pattern:(NSString *)pattern
{

    void (^validator)(NSString *) = ^(NSString *value) {
      if (minLength != nil) {
          if ([value length] < [minLength unsignedIntegerValue]) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must be at least %@ characters", [minLength stringValue]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }

      if (maxLength != nil) {
          if ([value length] > [maxLength unsignedIntegerValue]) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must be at most %@ characters", [minLength stringValue]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }

      if (pattern != nil && pattern.length != 0) {
          NSError *error;
          NSRegularExpression *re = [NSRegularExpression regularExpressionWithPattern:pattern options:0 error:&error];
          NSArray *matches = [re matchesInString:value options:0 range:NSMakeRange(0, [value length])];
          if ([matches count] == 0) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must match pattern \"%@\"", [re pattern]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }
    };

    return validator;
}

+ (void (^)(NSNumber *))numericValidator:(NSNumber *)minValue maxValue:(NSNumber *)maxValue
{
    void (^validator)(NSNumber *) = ^(NSNumber *value) {
      if (minValue != nil) {
          if ([value unsignedIntegerValue] < [minValue unsignedIntegerValue]) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must be at least %@", [minValue stringValue]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }

      if (maxValue != nil) {
          if ([value unsignedIntegerValue] > [maxValue unsignedIntegerValue]) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must be at most %@", [maxValue stringValue]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }
    };

    return validator;
}

+ (void (^)(NSArray<id> *))arrayValidator:(NSNumber *)minItems
                                 maxItems:(NSNumber *)maxItems
                            itemValidator:(void (^)(id))itemValidator
{
    void (^validator)(NSArray<id> *) = ^(NSArray<id> *value) {
      if (minItems != nil) {
          if ([value count] < [minItems unsignedIntegerValue]) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must be at least %@ items", [minItems stringValue]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }

      if (maxItems != nil) {
          if ([value count] > [maxItems unsignedIntegerValue]) {
              NSString *exceptionMessage =
                  [NSString stringWithFormat:@"value must be at most %@ items", [maxItems stringValue]];
              [[self class] raiseIllegalStateErrorWithMessage:exceptionMessage];
          }
      }

      if (itemValidator != nil) {
          for (id item in value) {
              itemValidator(item);
          }
      }
    };

    return validator;
}

+ (void (^)(NSDictionary<NSString *, id> *))mapValidator:(void (^)(id))itemValidator
{
    void (^validator)(NSDictionary<NSString *, id> *) = ^(NSDictionary<NSString *, id> *value) {
      if (itemValidator != nil) {
          for (id key in value) {
              itemValidator(value[key]);
          }
      }
    };

    return validator;
}

+ (void (^)(id))nullableValidator:(void (^)(id))internalValidator
{
    void (^validator)(id) = ^(id value) {
      if (value != nil) {
          internalValidator(value);
      }
    };

    return validator;
}

+ (void (^)(id))nonnullValidator:(void (^)(id))internalValidator
{
    void (^validator)(id) = ^(id value) {
      if (value == nil) {
          [[self class] raiseIllegalStateErrorWithMessage:@"Value must not be `nil`"];
      }

      if (internalValidator != nil) {
          internalValidator(value);
      }
    };

    return validator;
}

+ (void)raiseIllegalStateErrorWithMessage:(NSString *)message
{
    NSString *exceptionMessage =
        [NSString stringWithFormat:@"%@:\n%@", message, [[NSThread callStackSymbols] objectAtIndex:0]];
    [NSException raise:@"IllegalStateException" format:exceptionMessage, nil];
}
@end
