import 'package:exceptions/exceptions.dart';
import 'package:fast_equatable/fast_equatable.dart';

class FingerprintOverrides with FastEquatable {
  static final pattern = RegExp('([+-])([a-zA-Z_][a-zA-Z0-9_]{1,64})');

  final bool? allTargets;
  final Map<String, bool> targets;

  FingerprintOverrides(this.allTargets, this.targets);

  //Monitor https://searchfox.org/firefox-main/source/toolkit/components/resistfingerprinting/RFPTargetsDefault.inc
  FingerprintOverrides.defaults()
    : this(false, {
        'CanvasRandomization': true,
        'EfficientCanvasRandomization': true,
        'FontVisibilityLangPack': true,
        'JSMathFdlibm': true,
        'ScreenAvailToResolution': true,
        'NavigatorHWConcurrencyTiered': true,
        'MaxTouchPointsCollapse': true,
      });

  FingerprintOverrides.hardenedDefaults()
    : this(false, {
        'TouchEvents': true,
        'PointerEvents': true,
        'KeyboardEvents': true,
        'ScreenOrientation': true,
        'SpeechSynthesis': true,
        'CSSPrefersReducedMotion': true,
        'CSSPrefersContrast': true,
        'CanvasRandomization': true,
        'CanvasExtractionFromThirdPartiesIsBlocked': true,
        'JSLocale': true,
        'NavigatorAppVersion': true,
        'NavigatorBuildID': true,
        'NavigatorHWConcurrency': true,
        'NavigatorOscpu': true,
        'NavigatorPlatform': true,
        'NavigatorUserAgent': true,
        'PointerId': true,
        'StreamVideoFacingMode': true,
        'JSDateTimeUTC': true,
        'JSMathFdlibm': true,
        'Gamepad': true,
        'HttpUserAgent': true,
        'WindowOuterSize': true,
        'WindowScreenXY': true,
        'WindowInnerScreenXY': true,
        'ScreenPixelDepth': true,
        'ScreenRect': true,
        'ScreenAvailRect': true,
        'VideoElementMozFrames': true,
        'VideoElementMozFrameDelay': true,
        'VideoElementPlaybackQuality': true,
        'ReduceTimerPrecision': true,
        'WidgetEvents': true,
        'MediaDevices': true,
        'MediaCapabilities': true,
        'AudioSampleRate': true,
        'NetworkConnection': true,
        'WindowDevicePixelRatio': true,
        'MouseEventScreenPoint': true,
        'FontVisibilityBaseSystem': true,
        'FontVisibilityLangPack': true,
        'DeviceSensors': true,
        'RoundWindowSize': true,
        'UseStandinsForNativeColors': true,
        'AudioContext': true,
        'MediaError': true,
        'DOMStyleOsxFontSmoothing': true,
        'CSSDeviceSize': true,
        'CSSColorInfo': true,
        'CSSResolution': true,
        'CSSPrefersReducedTransparency': true,
        'CSSInvertedColors': true,
        'CSSVideoDynamicRange': true,
        'CSSPointerCapabilities': true,
        'WebGLRenderCapability': true,
        'WebGLRenderInfo': true,
        'SiteSpecificZoom': true,
        'FontVisibilityRestrictGenerics': true,
        'WebVTT': true,
        'WebGPULimits': true,
        'WebGPUIsFallbackAdapter': true,
        'WebGPUSubgroupSizes': true,
        'JSLocalePrompt': true,
        'ScreenAvailToResolution': true,
        'UseHardcodedFontSubstitutes': true,
        'DiskStorageLimit': true,
        'WebCodecs': true,
        'MaxTouchPoints': true,
        'MaxTouchPointsCollapse': true,
        'NavigatorHWConcurrencyTiered': true,
      });

  static Result<FingerprintOverrides> parse(
    String input,
    Set<String> availableTargets,
  ) {
    final cleaned = input.replaceAll(RegExp(r'\s'), '');
    if (cleaned.isEmpty) return Result.success(FingerprintOverrides(null, {}));

    bool? allTargets;
    final targets = <String, bool>{};

    for (final word in cleaned.split(',')) {
      final match = pattern.firstMatch(word);
      if (match == null) {
        return Result.failure(
          const ErrorMessage(source: 'FpParser', message: 'Invalid Override'),
        );
      }

      final enabled = match.group(1) != '-';
      final name = match.group(2)!;

      if (name == 'AllTargets') {
        allTargets = enabled;
        continue;
      }

      if (!availableTargets.contains(name)) {
        return Result.failure(
          const ErrorMessage(
            source: 'FpParser',
            message: 'Invalid target name',
          ),
        );
      }

      targets[name] = enabled;
    }

    return Result.success(FingerprintOverrides(allTargets, targets));
  }

  @override
  String toString() {
    final sb = StringBuffer();

    if (allTargets != null) {
      sb.write('${allTargets! ? '+' : '-'}AllTargets');
      if (targets.isNotEmpty) {
        sb.write(',');
      }
    }

    sb.write(
      targets.entries.map((e) => (e.value ? '+' : '-') + e.key).join(','),
    );

    return sb.toString();
  }

  FingerprintOverrides copyWithAllTargetsEnabled(bool value) {
    return FingerprintOverrides(
      value,
      Map.fromEntries(targets.entries.where((e) => e.value != value)),
    );
  }

  FingerprintOverrides copyWithTarget(String name, bool value) {
    if (value && allTargets == true) {
      return this;
    }

    return FingerprintOverrides(allTargets, {...targets, name: value});
  }

  @override
  List<Object?> get hashParameters => [allTargets, targets];
}
