import 'dart:math';

enum AcceptedTypes {
  string,
  integer,
  float,
  stringList,
  integerList,
  floatList,
}

class FuzzConfig {
  final String label;
  final AcceptedTypes type;
  final int minLength;
  final int maxLength;
  final num minValue;
  final num maxValue;

  FuzzConfig({
    required this.label,
    required this.type,
    this.minLength = 1,
    this.maxLength = 10,
    this.minValue = 0,
    this.maxValue = 100,
  });
}

class FuzzerResult {
  final String functionName;
  final List<dynamic> inputs;
  final dynamic output;
  final String message;

  FuzzerResult({
    required this.functionName,
    required this.inputs,
    this.output,
    required this.message,
  });
}

class Fuzzer {
  final List<FuzzConfig> configs;
  final int iterations;
  final Random _random = Random();

  Fuzzer({
    required this.configs,
    this.iterations = 3,
  });

  List<FuzzerResult> fuzz(Function fn) {
    final results = <FuzzerResult>[];
    final parameterCount = configs.length;

    for (int i = 0; i < iterations; i++) {
      final inputs = _generateInputs(parameterCount);
      try {
        final output = Function.apply(fn, inputs);
        results.add(FuzzerResult(
          functionName: fn.toString(),
          inputs: inputs,
          output: output,
          message: 'Execução bem-sucedida.',
        ));
      } catch (e) {
        results.add(FuzzerResult(
          functionName: fn.toString(),
          inputs: inputs,
          message: 'Erro: $e',
        ));
      }
    }
    return results;
  }

  List<dynamic> _generateInputs(int count) {
    return List.generate(count, (index) {
      final config = configs[index];
      return _generateValue(config);
    });
  }

  dynamic _generateValue(FuzzConfig config) {
    switch (config.type) {
      case AcceptedTypes.string:
        final length =
            _random.nextInt(config.maxLength - config.minLength + 1) +
                config.minLength;
        return _generateRandomString(length);
      case AcceptedTypes.integer:
        return _random.nextInt(
                config.maxValue.toInt() - config.minValue.toInt() + 1) +
            config.minValue.toInt();
      case AcceptedTypes.float:
        return _random.nextDouble() * (config.maxValue - config.minValue) +
            config.minValue;
      case AcceptedTypes.stringList:
        final length =
            _random.nextInt(config.maxLength - config.minLength + 1) +
                config.minLength;
        return List.generate(
            length, (_) => _generateRandomString(_random.nextInt(10) + 1));
      case AcceptedTypes.integerList:
        final length =
            _random.nextInt(config.maxLength - config.minLength + 1) +
                config.minLength;
        return List.generate(
            length,
            (_) =>
                _random.nextInt(
                    config.maxValue.toInt() - config.minValue.toInt() + 1) +
                config.minValue.toInt());
      case AcceptedTypes.floatList:
        final length =
            _random.nextInt(config.maxLength - config.minLength + 1) +
                config.minLength;
        return List.generate(
            length,
            (_) =>
                _random.nextDouble() * (config.maxValue - config.minValue) +
                config.minValue);
    }
  }

  String _generateRandomString(int length) {
    const chars =
        'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
    return String.fromCharCodes(
      Iterable.generate(
        length,
        (_) => chars.codeUnitAt(_random.nextInt(chars.length)),
      ),
    );
  }
}
