import 'dart:typed_data';
import 'parsing.dart';
import 'enums.dart';

class UuidValidation {
  /// Validates the provided [uuid] to make sure it has all the necessary
  /// components and formatting and returns a [bool]
  /// You can choose to validate from a string or from a byte list based on
  /// which parameter is passed.
  static bool isValidUUID(
      {String fromString = '',
      Uint8List? fromByteList,
      ValidationMode validationMode = ValidationMode.strictRFC9562,
      bool noDashes = false}) {
    if (fromByteList != null) {
      fromString = UuidParsing.unparse(fromByteList);
    }

    // UUID of all 0s is ok.
    if (fromString == Namespace.nil.value) {
      return true;
    }

    // UUID of all Fs is ok.
    if (fromString == Namespace.max.value) {
      return true;
    }

    // If its not 36 characters in length, don't bother (including dashes).
    if (!noDashes && fromString.length != 36) {
      return false;
    }

    // If its not 32 characters in length, don't bother (excluding excluding).
    if (noDashes && fromString.length != 32) {
      return false;
    }

    // Make sure if it passes the above, that it's a valid UUID or GUID.
    switch (validationMode) {
      // ignore: deprecated_member_use_from_same_package
      case ValidationMode.strictRFC9562 || ValidationMode.strictRFC4122:
        {
          var pattern = (noDashes)
              ? r'^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-8][0-9a-f]{3}-?[89ab][0-9a-f]{3}-?[0-9a-f]{12}$'
              : r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$';
          final regex = RegExp(pattern, caseSensitive: false, multiLine: true);
          final match = regex.hasMatch(fromString.toLowerCase());
          return match;
        }
      case ValidationMode.nonStrict:
        {
          var pattern = (noDashes)
              ? r'^[0-9a-f]{8}-?[0-9a-f]{4}-?[0-8][0-9a-f]{3}-?[0-9a-f]{4}-?[0-9a-f]{12}$'
              : r'^[0-9a-f]{8}-[0-9a-f]{4}-[0-8][0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$';
          final regex = RegExp(pattern, caseSensitive: false, multiLine: true);
          final match = regex.hasMatch(fromString.toLowerCase());
          return match;
        }
    }
  }

  /// Validates the provided [uuid] to make sure it has all the necessary
  /// components and formatting and throws a [FormatException] if it is invalid.
  /// You can choose to validate from a string or from a byte list based on
  /// which parameter is passed.
  /// Optionally you can set [validationMode] to `ValidationMode.nonStrict` to
  /// allow for non RFC4122 compliant UUIDs.
  /// If you are using a Microsoft GUID, you should set [validationMode] to
  /// `ValidationMode.nonStrict`.
  static void isValidOrThrow(
      {String fromString = '',
      Uint8List? fromByteList,
      ValidationMode validationMode = ValidationMode.strictRFC9562,
      bool noDashes = false}) {
    final isValid = isValidUUID(
        fromString: fromString,
        fromByteList: fromByteList,
        validationMode: validationMode,
        noDashes: noDashes);

    if (!isValid) {
      // let's check if it is a non RFC4122 uuid and help the developer
      if (validationMode != ValidationMode.nonStrict) {
        final isValidNonStrict = isValidUUID(
            fromString: fromString,
            fromByteList: fromByteList,
            validationMode: ValidationMode.nonStrict,
            noDashes: noDashes);

        if (isValidNonStrict) {
          throw FormatException(
              'The provided UUID is not RFC4122 compliant. It seems you might be using a Microsoft GUID. Try setting `validationMode = ValidationMode.nonStrict`',
              fromString);
        }
      }

      throw FormatException('The provided UUID is invalid.', fromString);
    }
  }
}
