import 'dart:convert';
import 'dart:io';

import 'package:analyzer/dart/element/element.dart';
import 'package:build/build.dart';
import 'package:code_builder/code_builder.dart';
import 'package:dart_style/dart_style.dart';
import 'package:glob/glob.dart';
import 'package:guru_spec/guru_spec.dart';
import 'package:guru_spec/src/tuple.dart';
import 'package:source_gen/source_gen.dart';
import 'package:yaml/yaml.dart';
import 'package:glob/list_local_fs.dart';

/// Created by Haoyi on 2023/1/19
Builder generatorFactoryBuilder(BuilderOptions options) =>
    SharedPartBuilder([GuruSpecGenerator()], "guru_spec");

class FieldProcessor {
  final dynamic _annotationType;
  final List<String> _validTypeNames;
  final Code Function(FieldElement element, ConstantReader annotation) assigner;

  FieldProcessor(this._annotationType, this._validTypeNames, this.assigner);
}

class GuruSpecGenerator extends GeneratorForAnnotation<GuruSpecCreator> {
  static const flavorField = "flavor";

  final Set<String> flavors = {};

  // method -> flavors
  final Map<String, Set<String>> iapIdsCheckMap = {};
  final Map<String, Set<String>> iapCapIdsCheckMap = {};
  final Map<String, String> remoteConfigKeys = {};

  final Map<String, Set<String>> idsMethodCheckMap = {};

  final Map<String, List<Parameter>> idsMethodParamsMap = {};

  final Map<String, Map<String, Code>> idsMethodMap = {};

  final Map<String, List<String>> productSkuParams = {};

  // final Map<String, >

  final Map<String, Set<String>> idsCheckMap = {};
  final Map<String, Set<String>> capIdsCheckMap = {};

  @override
  generateForAnnotatedElement(Element element, ConstantReader annotation, BuildStep buildStep) {
    if (element is! ExecutableElement) {
      final name = element.displayName;
      final todo = 'Remove the [GuruSpecCreator] annotation from `$name`.';
      throw InvalidGenerationSourceError(
        'Generator cannot target `$name`.\n[TODO] $todo',
        todo: 'Remove the [GuruSpecCreator] annotation from `$name`.',
      );
    }
    // if (!element.isAbstract) {
    //   final name = element.displayName;
    //   final todo = 'Please change class [$name] to `abstract` class.';
    //   throw InvalidGenerationSourceError(
    //     'Generator cannot target `$name`.\n[TODO] $todo',
    //     todo: todo,
    //   );
    // }
    // bool isAppSpec = false;
    // for (var type in element.allSupertypes) {
    //   if (type.getDisplayString(withNullability: false) == "AppSpecCreator") {
    //     isAppSpec = true;
    //     break;
    //   }
    // }
    // if (!isAppSpec) {
    //   final name = element.displayName;
    //   const todo = 'Please inherit the `AppSpec` class.';
    //   throw InvalidGenerationSourceError(
    //     'Generator cannot target `$name`.\n[TODO] $todo',
    //     todo: todo,
    //   );
    // }
    return _implementClass(element, annotation);
  }

  String _implementClass(ExecutableElement element, ConstantReader annotation) {
    log.info("_implementClass methods");

    final guruSpecFiles = Glob("guru/**guru_spec.yaml", recursive: true);
    // const className = "GuruSpecFactory";
    final StringBuffer codeString = StringBuffer();
    final list = guruSpecFiles.listSync();
    log.info("listSync list length:${list.length}");
    for (var entity in list) {
      log.info("listSync entity:${entity.path}");
      codeString.write(generateGuruSpecCode(File(entity.path)));
    }

    codeString.write(_buildGlobalRemoteConfigConstants());
    codeString.write(_buildGlobalExperiments());
    codeString.write(_buildGlobalProductIds());
    codeString.write(_buildGlobalProductCategoryConstants());
    codeString.write(_buildAppSpecFactory());
    codeString.write(_buildFlavors());
    return codeString.toString();
  }

  String _buildGlobalProductIds() {
    final methodList = <Method>[];
    for (var field in iapIdsCheckMap.entries) {
      final fieldName = field.key;
      final codeBlock = <Code>[];
      for (var flavor in flavors) {
        if (field.value.contains(flavor)) {
          codeBlock.add(Code("if (GuruApp.instance.flavor == '$flavor') {"));
          codeBlock.add(Code("return _${capitalize(flavor)}Products.$fieldName;"));
          codeBlock.add(const Code("}"));
        }
      }
      codeBlock.add(const Code("return ProductId.invalid;"));

      methodList.add(Method((method) {
        method
          ..name = fieldName
          ..static = true
          ..type = MethodType.getter
          ..returns = refer("ProductId")
          ..body = Block.of(codeBlock);
      }));
    }

    for (var field in idsMethodCheckMap.entries) {
      final fieldName = field.key;
      final codeBlock = <Code>[];
      final params = idsMethodParamsMap[fieldName];
      if (params == null) {
        continue;
      }
      for (var flavor in flavors) {
        if (field.value.contains(flavor)) {
          codeBlock.add(Code("if (GuruApp.instance.flavor == '$flavor') {"));
          codeBlock.add(Code(
              "return _${capitalize(flavor)}Products.$fieldName(${params.map((e) => e.name).join(',')});"));
          codeBlock.add(const Code("}"));
        }
      }
      codeBlock.add(const Code("return ProductId.invalid;"));

      methodList.add(Method((method) {
        method
          ..name = fieldName
          ..static = true
          ..requiredParameters.addAll(params)
          ..returns = refer("ProductId")
          ..body = Block.of(codeBlock);
      }));
    }

    for (var capability in _iapProductIdsCapabilities) {
      final fieldName = buildCapIds(capability);
      final codeBlock = <Code>[];
      for (var flavor in flavors) {
        if (iapCapIdsCheckMap[fieldName]?.contains(flavor) == true) {
          codeBlock.add(Code("if (GuruApp.instance.flavor == '$flavor') {"));
          codeBlock.add(Code("return _${capitalize(flavor)}Products.$fieldName;"));
          codeBlock.add(const Code("}"));
        }
      }
      codeBlock.add(const Code("return {};"));

      methodList.add(Method((method) {
        method
          ..name = fieldName
          ..static = true
          ..type = MethodType.getter
          ..returns = refer("Set<ProductId>")
          ..body = Block.of(codeBlock);
      }));
    }

    final codeBlock = <Code>[];
    // for (var flavor in flavors) {
    //   codeBlock.add(Code("if (GuruApp.instance.flavor == '$flavor') {"));
    //   codeBlock.add(Code("return _${capitalize(flavor)}ProductIds.oneOffChargeIapIds;"));
    //   codeBlock.add(const Code("}"));
    // }

    codeBlock.clear();

    codeBlock.clear();
    codeBlock.add(const Code("GuruApp.instance.productProfile.iapIds"));
    methodList.add(Method((method) {
      method
        ..name = "iapIds"
        ..static = true
        ..lambda = true
        ..type = MethodType.getter
        ..returns = refer("Set<ProductId>")
        ..body = Block.of(codeBlock);
    }));

    codeBlock.clear();
    codeBlock.add(const Code("GuruApp.instance.productProfile.oneOffChargeIapIds"));
    // codeBlock.add(const Code("return [];"));
    methodList.add(Method((method) {
      method
        ..name = "oneOffChargeIapIds"
        ..static = true
        ..lambda = true
        ..type = MethodType.getter
        ..returns = refer("Set<ProductId>")
        ..body = Block.of(codeBlock);
    }));

    codeBlock.clear();
    // for (var flavor in flavors) {
    //   codeBlock.add(Code("if (GuruApp.instance.flavor == '$flavor') {"));
    //   codeBlock.add(Code("return _${capitalize(flavor)}ProductIds.subscriptionsIapIds;"));
    //   codeBlock.add(const Code("}"));
    // }
    // codeBlock.add(const Code("return [];"));
    codeBlock.add(const Code("GuruApp.instance.productProfile.subscriptionsIapIds"));

    methodList.add(Method((method) {
      method
        ..name = "subscriptionsIapIds"
        ..static = true
        ..lambda = true
        ..type = MethodType.getter
        ..returns = refer("Set<ProductId>")
        ..body = Block.of(codeBlock);
    }));

    codeBlock.clear();
    codeBlock.add(const Code("GuruApp.instance.productProfile.pointsIapIds"));
    methodList.add(Method((method) {
      method
        ..name = "pointsIapIds"
        ..static = true
        ..lambda = true
        ..type = MethodType.getter
        ..returns = refer("Set<ProductId>")
        ..body = Block.of(codeBlock);
    }));

    codeBlock.clear();
    codeBlock.add(const Code("GuruApp.instance.productProfile.rewardIds"));
    methodList.add(Method((method) {
      method
        ..name = "rewardIds"
        ..static = true
        ..lambda = true
        ..type = MethodType.getter
        ..returns = refer("Set<ProductId>")
        ..body = Block.of(codeBlock);
    }));

    final classBuilder = Class((clazz) {
      clazz
        ..name = "ProductIds"
        ..methods.addAll(methodList);
    });

    final emitter = DartEmitter();
    return DartFormatter().format([classBuilder.accept(emitter)].join('\n\n'));
  }

  String _buildGlobalProductCategoryConstants() {
    final categoryClassBuilder = Class((clazz) {
      clazz
        ..name = 'ProductCategory'
        ..fields.addAll([...categoryFieldMap.values])
        ..methods.addAll([...categoryMethodMap.values]);
    });
    final emitter = DartEmitter();
    return DartFormatter().format([categoryClassBuilder.accept(emitter)].join('\n\n'));
  }

  String _buildGlobalRemoteConfigConstants() {
    final List<Field> fieldList = <Field>[];
    final List<Method> methodList = <Method>[];

    for (var fieldEntry in remoteConfigKeys.entries) {
      final fieldValue = fieldEntry.key;
      final fieldName = fieldEntry.value;

      fieldList.add(Field((field) => field
        ..name = fieldName
        ..static = true
        ..assignment = Code("'$fieldValue'")
        ..modifier = FieldModifier.constant));
    }

    final classBuilder = Class((clazz) {
      clazz
        ..name = 'RemoteConfigConstants'
        ..fields.addAll(fieldList);
    });
    final emitter = DartEmitter();
    return DartFormatter().format([classBuilder.accept(emitter)].join('\n\n'));
  }

  String _buildGlobalExperiments() {
    final List<Method> methodList = <Method>[];
    final codeBlocks = <Code>[];

    for (var flavor in flavors) {
      codeBlocks.add(Code("if (GuruApp.instance.flavor == '$flavor') {"));
      codeBlocks.add(
          Code("return _${capitalize(flavor, useCamelCase: true)}ABTestExperiments.experiments;"));
      codeBlocks.add(const Code("}"));
    }
    codeBlocks.add(const Code("return {};"));

    methodList.add(Method((method) {
      method
        ..name = "experiments"
        ..static = true
        ..type = MethodType.getter
        ..returns = refer("Map<String, ABTestExperiment>")
        ..body = Block.of(codeBlocks);
    }));

    final classBuilder = Class((clazz) {
      clazz
        ..name = 'ABTestExperimentConstants'
        ..methods.addAll(methodList);
    });
    final emitter = DartEmitter();
    return DartFormatter().format([classBuilder.accept(emitter)].join('\n\n'));
  }

  String _buildAppSpecFactory() {
    final creator = <Code>[];
    for (var flavor in flavors) {
      creator.add(Code("if (flavor == '$flavor') {"));
      creator.add(Code("return _${capitalize(flavor)}AppSpec._instance;"));
      creator.add(const Code("}"));
    }

    creator.add(const Code("throw NotImplementationAppSpecCreatorException();"));

    final classBuilder = Class((clazz) {
      clazz
        ..name = "_GuruSpecFactory"
        ..methods.add(Method((method) {
          method
            ..name = "create"
            ..static = true
            ..requiredParameters.addAll([
              Parameter((p) => p
                ..type = refer("String")
                ..name = "flavor")
            ])
            ..returns = refer("AppSpec")
            ..body = Block.of(creator);
        }));
    });

    final emitter = DartEmitter();
    return DartFormatter().format([classBuilder.accept(emitter)].join('\n\n'));
  }

  String _buildFlavors() {
    final List<Field> fieldList = <Field>[];
    for (var flavor in flavors) {
      fieldList.add(Field((field) => field
        ..name = camelCase(flavor)
        ..static = true
        ..assignment = Code("'$flavor'")
        ..modifier = FieldModifier.constant));
    }
    final classBuilder = Class((clazz) {
      clazz
        ..name = "Flavors"
        ..fields.addAll(fieldList);
    });

    final emitter = DartEmitter();
    return DartFormatter().format([classBuilder.accept(emitter)].join('\n\n'));
  }

  String generateGuruSpecCode(File file) {
    final yaml = file.readAsStringSync();
    final jsonStr = json.encode(loadYaml(yaml));
    final yamlMap = json.decode(jsonStr);

    log.info("doc:$yamlMap");

    final flavor = yamlMap['flavor'] ?? "Main";

    final className = "${capitalize(flavor)}AppSpec";

    flavors.add(flavor);

    // final iapClassBuilder = buildIapProductIds(yamlMap['iap_profile'] ?? {}, flavor: flavor);

    try {
      final productsClassBuilders = buildProducts(yamlMap['products'] ?? {}, flavor: flavor);

      final experimentClassBuilders =
          buildExperiments(yamlMap['experiments'] ?? {}, flavor: flavor);

      // final productManifestClassBuilder =
      //     _buildProductManifest(flavor, yamlMap['product_manifest'] ?? {});

      final remoteConfigConstantsBuilder =
          buildRemoteConfigConstantsClass(yamlMap['remote_config'], flavor: flavor);

      final classBuilder = Class((clazz) {
        clazz
          ..name = '_$className'
          ..constructors.add(buildAppSpecConstructor())
          ..fields.addAll([
            Field((field) => field
              ..name = "_instance"
              ..static = true
              ..assignment = Code("_$className._()")
              ..modifier = FieldModifier.final$),
            _buildAppName(yamlMap['app_name']),
            _buildAppCategory(yamlMap['app_category'] ?? "game"),
            _buildFlavor(yamlMap['flavor'] ?? "Main"),
            _buildAppDetails(yamlMap['details']),
            _buildDeployment(yamlMap['deployment']),
            _buildAdsProfile(yamlMap['ads_profile'], yamlMap['details'] ?? {}),
            _buildProductProfile(flavor: flavor),
            _buildAdjustProfile(yamlMap['adjust_profile'] ?? {}),
            _buildDefaultRemoteConfig(flavor: flavor),
            _buildABTestExperiments(flavor: flavor)
          ])
          ..methods.addAll([_buildGetRemoteConfigKey(flavor: flavor)])
          ..extend = refer("AppSpec");
      });
      final emitter = DartEmitter();

      final StringBuffer codeString = StringBuffer();
      codeString.write(
          DartFormatter().format([remoteConfigConstantsBuilder.accept(emitter)].join('\n\n')));
      // codeString.write(DartFormatter().format([iapClassBuilder.accept(emitter)].join('\n\n')));
      codeString.write(DartFormatter().format([classBuilder.accept(emitter)].join('\n\n')));
      // codeString
      //     .write(DartFormatter().format([productManifestClassBuilder.accept(emitter)].join('\n\n')));
      for (var productsClassBuilder in productsClassBuilders) {
        codeString
            .write(DartFormatter().format([productsClassBuilder.accept(emitter)].join('\n\n')));
      }
      codeString
          .write(DartFormatter().format([experimentClassBuilders.accept(emitter)].join('\n\n')));
      return codeString.toString();
    } catch (error, stacktrace) {
      log.info("error:$stacktrace");
      rethrow;
    }
  }

  Constructor buildAppSpecConstructor() => Constructor((c) => c..name = "_");

  Field _buildAppName(String name) {
    return Field((field) => field
      ..name = "appName"
      ..static = false
      ..assignment = Code("'$name'")
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  Field _buildAppCategory(String name) {
    return Field((field) => field
      ..name = "appCategory"
      ..static = false
      ..assignment = Code('AppCategory.$name')
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  Field _buildFlavor(String flavor) {
    return Field((field) => field
      ..name = "flavor"
      ..static = false
      ..assignment = Code("'$flavor'")
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  static const _appDetailsFieldMapping = {
    "saas_app_id": "saasAppId",
    "authority": "authority",
    "storage_prefix": "storagePrefix",
    "default_cdn_prefix": "defaultCdnPrefix",
    "android_gp_url": "androidGooglePlayUrl",
    "ios_spp_store_url": "iosAppStoreUrl",
    "policy_url": "policyUrl",
    "terms_url": "termsUrl",
    "email_url": "emailUrl",
    "package_name": "packageName",
    "bundle_id": "bundleId",
    "facebook_app_id": "facebookAppId"
  };

  static final appDetails = <String, String>{};

  Field _buildAppDetails(Map<String, dynamic> detailsMap) {
    final codeBlocks = <Code>[const Code("AppDetails(")];
    for (var fieldEntry in _appDetailsFieldMapping.entries) {
      final fieldName = _appDetailsFieldMapping[fieldEntry.key];
      final fieldData = detailsMap[fieldEntry.key];
      if (fieldName != null && fieldData != null) {
        codeBlocks.add(Code("$fieldName: '$fieldData',"));
      }
    }
    codeBlocks.add(const Code(")"));

    return Field((field) => field
      ..name = "details"
      ..static = false
      ..assignment = Block.of(codeBlocks)
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  static const _deploymentFieldMapping = {
    "property_cache_size": "propertyCacheSize",
    "enable_dithering": "enableDithering",
    "disable_rewards_ads": "disableRewardsAds",
    "enable_analytics_statistic": "enableAnalyticsStatistic",
    "auto_restore_iap": "autoRestoreIap",
    "igc_balance_secret": "igcBalanceSecret",
    "init_igc": "initIgc",
    "auto_request_notification_permission": "autoRequestNotificationPermission",
    "log_file_size_limit": "logFileSizeLimit",
    "log_file_count": "logFileCount",
    "persistent_log_level": "persistentLogLevel",
    "ios_validate_receipt_password": "iosValidateReceiptPassword",
    "conversion_events": "conversionEvents",
    "api_connect_timeout": "apiConnectTimeout",
    "api_receive_timeout": "apiReceiveTimeout",
    "ios_sandbox_subs_renewal_speed": "iosSandboxSubsRenewalSpeed",
    "ads_compliant_initialization": "adsCompliantInitialization",
    "notification_permission_prompt_trigger": "notificationPermissionPromptTrigger",
    "tracking_notification_permission_pass": "trackingNotificationPermissionPass",
    "tracking_notification_permission_pass_limit_times":
        "trackingNotificationPermissionPassLimitTimes",
    "allow_interstitial_as_alternative_reward": "allowInterstitialAsAlternativeReward",
    "show_internal_ads_when_banner_unavailable": "showInternalAdsWhenBannerUnavailable",
    "subscription_restore_grace_count": "subscriptionRestoreGraceCount",
    "fullscreen_ads_min_interval": "fullscreenAdsMinInterval",
    "enabled_sync_account_profile": "enabledSyncAccountProfile",
    "purchase_event_trigger": "purchaseEventTrigger"
  };

  final RegExp numericOrBoolRegex = RegExp(r'^(true|false|-?\d+)$');
  final enumFieldMap = <String, Tuple2<String, Set<String>>>{
    "notificationPermissionPromptTrigger":
        const Tuple2("PromptTrigger", <String>{"rationale", "request"}),
  };

  Field _buildDeployment(Map<String, dynamic>? deploymentMap) {
    late List<Code> codeBlocks;
    if (deploymentMap == null || deploymentMap.isEmpty) {
      codeBlocks = <Code>[const Code("Deployment.fromJson({})")];
    } else {
      codeBlocks = <Code>[const Code("Deployment(")];
      for (var fieldEntry in _deploymentFieldMapping.entries) {
        final fieldName = _deploymentFieldMapping[fieldEntry.key];
        final fieldData = deploymentMap[fieldEntry.key];
        if (fieldName != null && fieldData != null) {
          if (enumFieldMap.containsKey(fieldName)) {
            final tuple = enumFieldMap[fieldName]!;
            if (tuple.item2.contains(fieldData)) {
              codeBlocks.add(Code("$fieldName: ${enumFieldMap[fieldName]!.item1}.$fieldData,"));
            }
          } else if (fieldData is String && !numericOrBoolRegex.hasMatch(fieldData)) {
            codeBlocks.add(Code("$fieldName: '$fieldData',"));
          } else if (fieldEntry.key == "conversion_events" && fieldData is List) {
            codeBlocks.add(Code("$fieldName: {"));
            codeBlocks.addAll(fieldData.map((value) => Code("'$value',")));
            codeBlocks.add(const Code("},"));
          } else {
            codeBlocks.add(Code("$fieldName: $fieldData,"));
          }
        }
      }
      codeBlocks.add(const Code(")"));
    }
    return Field((field) => field
      ..name = "deployment"
      ..static = false
      ..assignment = Block.of(codeBlocks)
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  static const _adsProfileFieldMapping = {
    "banner_ad_unit_id": "bannerId",
    "interstitial_ad_unit_id": "interstitialId",
    "rewards_ad_unit_id": "rewardsId",
    "amz_app_id": "amazonAppId",
    "banner_amz_slot_id": "amazonBannerSlotId",
    "interstitial_amz_slot_id": "amazonInterstitialSlotId",
    "rewarded_amz_slot_id": "amazonRewardedSlotId"
  };

  static const _adsIdsMapping = {
    "banner_ad_unit_id": "AdUnitId",
    "interstitial_ad_unit_id": "AdUnitId",
    "rewards_ad_unit_id": "AdUnitId",
    "amz_app_id": "AdAppId",
    "banner_amz_slot_id": "AdSlotId",
    "interstitial_amz_slot_id": "AdSlotId",
    "rewarded_amz_slot_id": "AdSlotId"
  };

  static const _iapProductIdsCapabilities = {"noAds"};
  static const productIdsCapabilities = {"noAds"};

  static String buildCapIds(String cap) => "${cap}CapIds";

  Field _buildAdsProfile(Map<String, dynamic>? map, Map<String, dynamic> detailsMap) {
    late List<Code> codeBlocks = <Code>[const Code("AdsProfile(")];
    if (map == null || map.isEmpty) {
      codeBlocks = <Code>[const Code("AdsProfile.invalid")];
    } else {
      codeBlocks = <Code>[const Code("AdsProfile(")];
      for (var fieldEntry in _adsProfileFieldMapping.entries) {
        final fieldName = _adsProfileFieldMapping[fieldEntry.key];
        final fieldType = _adsIdsMapping[fieldEntry.key];
        final fieldData = map[fieldEntry.key];

        if (fieldName != null && fieldType != null && fieldData != null) {
          final android = fieldData['android'] ?? '';
          final ios = fieldData['ios'] ?? '';
          codeBlocks.add(Code("$fieldName: const $fieldType(android: '$android', ios: '$ios'),"));
        }
      }
      codeBlocks.add(Code(
          "pubmaticAppStoreUrl: Platform.isAndroid ? '${detailsMap["android_gp_url"] ?? ""}' : '${detailsMap["ios_spp_store_url"] ?? ""}'"));
      codeBlocks.add(const Code(")"));
    }

    return Field((field) => field
      ..name = "adsProfile"
      ..static = false
      ..assignment = Block.of(codeBlocks)
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  // Field _buildProductProfile({String flavor = ""}) {
  //   final idsFieldName = "_${capitalize(flavor)}IapProductIds";
  //   final codeBlocks = <Code>[const Code("IapProfile(")];
  //   codeBlocks.add(Code("oneOffChargeIapIds: $idsFieldName.oneOffChargeIapIds,"));
  //   codeBlocks.add(Code("subscriptionsIapIds: $idsFieldName.subscriptionsIapIds,"));
  //   for (var cap in _iapProductIdsCapabilities) {
  //     final fieldName = buildCapIds(cap);
  //     if (iapCapIdsCheckMap.containsKey(fieldName)) {
  //       codeBlocks.add(Code("$fieldName: $idsFieldName.$fieldName,"));
  //     }
  //   }
  //   codeBlocks.add(const Code(")"));
  //
  //   return Field((field) => field
  //     ..name = "iapProfile"
  //     ..static = false
  //     ..assignment = Block.of(codeBlocks)
  //     ..annotations.add(const CodeExpression(Code('override')))
  //     ..modifier = FieldModifier.final$);
  // }

  Field _buildProductProfile({String flavor = ""}) {
    final idsFieldName = "_${capitalize(flavor)}Products";
    final codeBlocks = <Code>[const Code("ProductProfile(")];
    codeBlocks.add(Code("oneOffChargeIapIds: $idsFieldName.oneOffChargeIapIds,"));
    codeBlocks.add(Code("subscriptionsIapIds: $idsFieldName.subscriptionsIapIds,"));
    codeBlocks.add(Code("pointsIapIds: $idsFieldName.pointsIapIds,"));
    codeBlocks.add(Code("rewardIds: $idsFieldName.rewardIds,"));
    codeBlocks.add(Code("igcIds: $idsFieldName.igcIds,"));
    codeBlocks.add(Code("groupMap: $idsFieldName.groupMap,"));
    codeBlocks.add(Code("manifestBuilders: $idsFieldName.manifestBuilders,"));
    for (var cap in _iapProductIdsCapabilities) {
      final fieldName = buildCapIds(cap);
      if (iapCapIdsCheckMap.containsKey(fieldName)) {
        codeBlocks.add(Code("$fieldName: $idsFieldName.$fieldName,"));
      }
    }
    codeBlocks.add(const Code(")"));

    return Field((field) => field
      ..name = "productProfile"
      ..static = false
      ..assignment = Block.of(codeBlocks)
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  Field _buildDefaultRemoteConfig({String flavor = ""}) {
    return Field((field) => field
      ..name = "defaultRemoteConfig"
      ..static = false
      ..assignment = Code("_${capitalize(flavor)}RemoteConfigConstants._defaultConfigs")
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  Method _buildGetRemoteConfigKey({String flavor = ""}) {
    final body = Code("_${capitalize(flavor)}RemoteConfigConstants.getKey(key)");
    return Method((method) {
      method
        ..name = "getRemoteConfigKey"
        ..annotations.add(const CodeExpression(Code('override')))
        ..requiredParameters.addAll([
          Parameter((p) => p
            ..type = refer("String")
            ..name = 'key')
        ])
        ..returns = refer("String")
        ..lambda = true
        ..body = body;
    });
  }

  Field _buildABTestExperiments({String flavor = ""}) {
    return Field((field) => field
      ..name = "localABTestExperiments"
      ..static = false
      ..assignment = Code("_${capitalize(flavor, useCamelCase: true)}ABTestExperiments.experiments")
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  Field _buildAdjustProfile(Map<String, dynamic> map) {
    final codeBlocks = <Code>[const Code("AdjustProfile(")];
    final appTokenMap = map['app_token'];
    if (appTokenMap != null) {
      codeBlocks.add(Code(
          "appToken: Platform.isAndroid ? '${appTokenMap['android'] ?? ''}' : '${appTokenMap['ios'] ?? ''}',"));
    } else {
      codeBlocks.add(const Code("appToken: '',"));
    }
    final eventMap = map['event_map'] ?? {};

    final androidMapCodeBlocks = <Code>[];
    final iosMapCodeBlocks = <Code>[];

    Code? generateCode(String event, dynamic token, bool hasParams) {
      if (token != null && token != '') {
        if (hasParams == true) {
          return Code(
              "\"$event\": (params)=> AdjustProfile.createAdjustEvent(\"$token\", params),");
        }
        return Code("\"$event\": (_) => AdjustEvent(\"$token\"),");
      }
      return null;
    }

    if (eventMap is Map) {
      for (var entry in eventMap.entries) {
        final map = entry.value;
        final androidToken = map['android'];
        final iosToken = map['ios'];
        final hasParams = (map['revenue'] == true) || map["params"] == true;

        final androidCode = generateCode(entry.key, androidToken, hasParams);
        if (androidCode != null) {
          androidMapCodeBlocks.add(androidCode);
        }

        final iosCode = generateCode(entry.key, iosToken, hasParams);
        if (iosCode != null) {
          iosMapCodeBlocks.add(iosCode);
        }
      }
    }
    codeBlocks
        .add(const Code("eventNameMapping: Platform.isAndroid ? <String, AdjustEventConverter>{"));
    codeBlocks.addAll(androidMapCodeBlocks);
    codeBlocks.add(const Code("} : <String, AdjustEventConverter>{"));
    codeBlocks.addAll(iosMapCodeBlocks);
    codeBlocks.add(const Code("})"));

    return Field((field) => field
      ..name = "adjustProfile"
      ..static = false
      ..assignment = Block.of(codeBlocks)
      ..annotations.add(const CodeExpression(Code('override')))
      ..modifier = FieldModifier.final$);
  }

  static String camelCase(String value) {
    if (value == '') {
      return '';
    }

    final separatedWords = value.split(RegExp(r'[!@#<>?":`~;[\]\\|=+)(*&^%-\s_]+'));
    var newString = '';

    for (final word in separatedWords) {
      newString += word[0].toUpperCase() + word.substring(1).toLowerCase();
    }

    return newString[0].toLowerCase() + newString.substring(1);
  }

  //
  String capitalize(String text, {bool useCamelCase = false}) {
    final value = useCamelCase ? camelCase(text) : text;
    switch (value.length) {
      case 0:
        return value;
      case 1:
        return value.toUpperCase();
      default:
        return value.substring(0, 1).toUpperCase() + value.substring(1);
    }
  }

  //
  // List<Field> _buildIapProductField(Map<String, dynamic> map, String flavor) {
  //   final List<Field> fieldList = <Field>[];
  //   // String? noAds;
  //   final List<String> oneOffChargeIapIds = [];
  //   final List<String> subscriptionsIapIds = [];
  //   final Map<String, Set<String>> capabilitiesIapIds = {};
  //   for (var fieldEntry in map.entries) {
  //     String fieldName = camelCase(fieldEntry.key);
  //     final fieldData = fieldEntry.value;
  //     if (fieldName != '' && fieldData != null) {
  //       final android = fieldData['android'] ?? '';
  //       final ios = fieldData['ios'] ?? '';
  //       final attr = fieldData['attr'] ?? '';
  //       final capabilities = (fieldData["capabilities"] ?? '') as String;
  //
  //       if (capabilities != '') {
  //         final segments = capabilities.split('|');
  //         final capabilitiesList =
  //             segments.where((segment) => _iapProductIdsCapabilities.contains(segment)).toList();
  //         for (var capability in capabilitiesList) {
  //           capabilitiesIapIds[capability] = (capabilitiesIapIds[capability] ??= <String>{})
  //             ..add(fieldName);
  //         }
  //       }
  //
  //       if (attr == 'subscriptions') {
  //         subscriptionsIapIds.add(fieldName);
  //       } else {
  //         oneOffChargeIapIds.add(fieldName);
  //       }
  //
  //       fieldList.add(Field((field) => field
  //         ..name = fieldName
  //         ..static = true
  //         ..assignment =
  //             Code("ProductId(android: '$android', ios: '$ios', attr: TransactionAttributes.$attr)")
  //         ..modifier = FieldModifier.constant));
  //
  //       iapIdsCheckMap[fieldName] = (iapIdsCheckMap[fieldName] ??= {})..add(flavor);
  //     }
  //   }
  //
  //   for (var entry in capabilitiesIapIds.entries) {
  //     final fieldName = buildCapIds(entry.key);
  //     if (entry.value.isNotEmpty) {
  //       fieldList.add(Field((field) => field
  //         ..name = fieldName
  //         ..static = true
  //         ..assignment = Code("<ProductId>[${entry.value.join(',')}]")
  //         ..modifier = FieldModifier.constant));
  //       iapCapIdsCheckMap[fieldName] = (iapCapIdsCheckMap[fieldName] ??= {})..add(flavor);
  //     }
  //   }
  //
  //   fieldList.add(Field((field) => field
  //     ..name = "oneOffChargeIapIds"
  //     ..static = true
  //     ..assignment = Code("<ProductId>[${oneOffChargeIapIds.join(',')}]")
  //     ..modifier = FieldModifier.constant));
  //
  //   fieldList.add(Field((field) => field
  //     ..name = "subscriptionsIapIds"
  //     ..static = true
  //     ..assignment = Code("<ProductId>[${subscriptionsIapIds.join(',')}]")
  //     ..modifier = FieldModifier.constant));
  //
  //   return fieldList;
  // }

  List<Method> buildIapProfileMethods() {
    final List<Method> methodList = <Method>[];
    methodList.add(Method((method) => method
      ..name = 'iapIds'
      ..type = MethodType.getter
      ..returns = refer('Set<ProductId>')
      ..lambda = true
      ..body = const Code("{...oneOffChargeIapIds, ...subscriptionsIapIds}")
      ..static = true));
    return methodList;
  }

  static final Map<String, Field> categoryFieldMap = <String, Field>{};
  static final Map<String, Method> categoryMethodMap = <String, Method>{};
  static final Map<String, String> categoryFieldNameMap = <String, String>{};

  static const conditionOpts = {
    "eq": "equals",
    "gt": "greaterThan",
    "gte": "greaterThanOrEquals",
    "lt": "lessThan",
    "lte": "lessThanOrEquals",
    "ne": "notEquals"
  };

  List<Class> buildProducts(Map<String, dynamic> map, {String flavor = ""}) {
    final List<Field> regExpField = <Field>[];
    final List<Field> fieldList = <Field>[];

    final List<Method> methodList = <Method>[];
    final List<String> manifestBuilders = <String>[];
    // String? noAds;
    final List<String> oneOffChargeIapIds = [];
    final List<String> subscriptionsIapIds = [];
    final List<String> pointsIapIds = [];
    final List<String> rewardIds = [];
    final List<String> igcIds = [];
    final Map<String, Set<String>> capabilitiesIds = {};
    final Set<String> groups = {};
    final Map<String, Set<String>> groupSubsIds = {}; // group key
    final Map<String, Set<String>> productOfferIds = {};

    for (var fieldEntry in map.entries) {
      String fieldName = camelCase(fieldEntry.key);
      final fieldData = fieldEntry.value;
      if (fieldName == '' || fieldData == null) {
        continue;
      }

      final attr = fieldData['attr'] ?? '';
      final capabilities = (fieldData["capabilities"] ?? '') as String;
      final android = fieldData['android'] ?? '';
      final ios = fieldData['ios'] ?? '';
      final sku = (fieldData['sku'] ?? '') as String;
      final methods = ((fieldData['method'] ?? '') as String).split(",");
      final manifest = (fieldData['manifest'] ?? <String, dynamic>{}) as Map<String, dynamic>;
      final offers = fieldData['offers'] ?? <String>[];
      final basePlan = fieldData['base_plan'] ?? '';
      final group = fieldData['group'] ?? '';
      final points =
          ((fieldData['points'] ?? false) as bool) && (attr == 'consumable') ? true : false;
      log.info(
          "fieldName:$fieldName fieldData:${fieldData['attr']} points:$points(${fieldData['points']})");
      if (attr == 'subscriptions') {
        if (group.isEmpty == true) {
          final todo =
              'Please define `group` field to subscriptions [$fieldName] in guru_spec.yaml.';
          throw InvalidGenerationSourceError(
            'Generator cannot target `$fieldName`.\n[TODO] $todo',
            todo: todo,
          );
        }
        groups.add(group);
      }

      void addCapabilities(String name) {
        final segments = capabilities.split('|');
        final capabilitiesList =
            segments.where((segment) => productIdsCapabilities.contains(segment)).toList();
        for (var capability in capabilitiesList) {
          capabilitiesIds[capability] = (capabilitiesIds[capability] ??= <String>{})..add(name);
        }
      }

      final hasOfferSubs =
          attr == 'subscriptions' && group.isNotEmpty && basePlan.isNotEmpty && offers.isNotEmpty;

      if (capabilities != '') {
        addCapabilities(fieldName);
      }

      final List<Parameter> params = [];
      final List<String> skuParams = [];
      final List<Parameter> ownParams = [];
      // methodList.add(_buildProductManifestMethod(sku, fieldName, manifest));
      if (paramsExp.hasMatch(sku)) {
        final str = sku.replaceAllMapped(paramsExp, (match) {
          final param = match.group(1) ?? "";
          final paramName = camelCase(param);
          params.add(Parameter((p) => p
            ..type = refer("String")
            ..name = paramName));
          skuParams.add(param);
          return "\${$paramName}";
        });

        final regExp = sku.replaceAll(paramsExp, "(.*)");

        regExpField.add(Field((field) => field
          ..name = "${fieldName}RegExp"
          ..static = true
          ..assignment = Code("RegExp(r\"^$regExp\$\")")
          ..modifier = FieldModifier.final$));

        ownParams.addAll(params);

        if (methods.length > 1) {
          params.add(Parameter((p) => p
            ..type = refer("TransactionMethod")
            ..name = 'method'));
        }

        methodList.add(Method((method) => method
          ..name = fieldName
          ..returns = refer('ProductId')
          ..lambda = true
          ..requiredParameters.addAll(params)
          ..static = true
          ..body = Code(
              "GuruApp.instance.defineProductId('$str', TransactionAttributes.$attr, ${methods.length > 1 ? 'method' : 'TransactionMethod.${methods.first}'})")));
        idsMethodCheckMap[fieldName] = (idsMethodCheckMap[fieldName] ??= {})..add(flavor);
        idsMethodParamsMap[fieldName] = params;
      } else {
        for (var method in methods) {
          if (method == 'iap') {
            if (attr == 'subscriptions') {
              // if (!hasOfferSubs) {
              subscriptionsIapIds.add(fieldName);
              groupSubsIds[group] = (groupSubsIds[group] ?? <String>{})..add(fieldName);
              // }
            } else {
              oneOffChargeIapIds.add(fieldName);
            }
          } else if (method == 'reward') {
            rewardIds.add(fieldName);
          } else if (method == 'igc') {
            igcIds.add(fieldName);
          }
        }

        if (hasOfferSubs) {
          final offerIds = <String>[];
          for (var offer in offers) {
            final offerProductName = camelCase("${group}_${basePlan}_$offer");
            offerIds.add(offerProductName);
            fieldList.add(Field((field) => field
              ..name = offerProductName
              ..static = true
              ..assignment = Code(
                  "ProductId(android: '$android', ios: '$ios', attr: TransactionAttributes.$attr, basePlan: '$basePlan', offerId: '$offer', originId: $fieldName)")
              ..modifier = FieldModifier.constant));
            iapIdsCheckMap[offerProductName] = (iapIdsCheckMap[offerProductName] ??= {})
              ..add(flavor);
            addCapabilities(offerProductName);
            groupSubsIds[group] = (groupSubsIds[group] ?? <String>{})..add(offerProductName);
            subscriptionsIapIds.add(offerProductName);
            productOfferIds[fieldName] = (productOfferIds[fieldName] ?? <String>{})
              ..add(offerProductName);
          }

          fieldList.add(Field((field) => field
            ..name = "${fieldName}OfferIds"
            ..static = true
            ..assignment = Code("<ProductId>{${offerIds.join(',')}}")
            ..modifier = FieldModifier.final$));
        }
        fieldList.add(Field((field) => field
          ..name = fieldName
          ..static = true
          ..assignment = sku.isNotEmpty
              ? Code(
                  "ProductId.fromSku(sku: '$sku', attr: TransactionAttributes.$attr, points: $points)")
              : Code(
                  "ProductId(android: '$android', ios: '$ios',${basePlan.toString().isNotEmpty ? "basePlan: '$basePlan', " : ''}attr: TransactionAttributes.$attr, points: $points)")
          ..modifier = FieldModifier.constant));
        if (points) {
          pointsIapIds.add(fieldName);
        }
        iapIdsCheckMap[fieldName] = (iapIdsCheckMap[fieldName] ??= {})..add(flavor);
      }

      if (manifest.isNotEmpty) {
        String category = (manifest.remove("category") ?? '') as String;
        if (category.isNotEmpty != true) {
          final todo = 'Please define `category` field to [$fieldName] in guru_spec.yaml.';
          throw InvalidGenerationSourceError(
            'Generator cannot target `$fieldName`.\n[TODO] $todo',
            todo: todo,
          );
        }
        if (fillParamsExp.hasMatch(category)) {
          // skuParams
          final Map<String, Parameter> categoryParams = <String, Parameter>{};
          final oldCategory = category;
          category = category.replaceAllMapped(fillParamsExp, (match) {
            // categoryParams =
            final skuParamsIdx = int.parse(match.group(1)!) - 1;
            log.info("skuParamsIdx:$skuParamsIdx skuParams:${skuParams.length}");
            final categoryParamName = skuParams[skuParamsIdx];
            if (!categoryParams.containsKey(categoryParamName)) {
              categoryParams[categoryParamName] = Parameter((p) => p
                ..type = refer("String")
                ..name = camelCase(categoryParamName));
            }
            return "\${matches.first.group(${match.group(1)!})!}";
          });

          if (!categoryMethodMap.containsKey(fieldName)) {
            final categoryCode = oldCategory.replaceAllMapped(fillParamsExp, (match) {
              final skuParamsIdx = int.parse(match.group(1)!) - 1;
              final categoryParamName = camelCase(skuParams[skuParamsIdx]);
              return "\${$categoryParamName}";
            });
            log.info("[$fieldName] categoryParams:${categoryParams.length}");
            categoryMethodMap[fieldName] = Method((method) => method
              ..name = fieldName
              ..static = true
              ..requiredParameters.addAll(categoryParams.values.toList())
              ..body = Code("\"$categoryCode\";"));
          }
        } else {
          if (!categoryFieldMap.containsKey(category)) {
            final categoryFieldName = camelCase(category);
            if (categoryFieldNameMap.containsKey(categoryFieldName.toLowerCase())) {
              final todo =
                  '"`$category` Category and `${categoryFieldNameMap[categoryFieldName]}` Category are defined too similarly. In order to facilitate code comprehension, please differentiate the two categories by adding more specific subcategories, such as Category`${category}1`."';
              throw InvalidGenerationSourceError(
                'Generator cannot target `$fieldName`.\n[TODO] $todo',
                todo: todo,
              );
            }
            categoryFieldNameMap[categoryFieldName.toLowerCase()] = category;
            categoryFieldMap[category] = Field((field) => field
              ..name = categoryFieldName
              ..static = true
              ..assignment = Code("'$category'")
              ..modifier = FieldModifier.constant);
          }
        }

        final method = _buildProductManifestMethod(
            flavor, fieldName, category, sku, hasOfferSubs, skuParams, manifest);
        manifestBuilders.add(method.name!);
        methodList.add(method);

        if (attr == "possessive" || attr == 'asset') {
          final ownCodeBlock = <Code>[];
          ownCodeBlock.add(const Code("if (entity.state == TransactionState.success &&"));
          ownCodeBlock.add(Code("entity.category == '${fieldEntry.key}') {"));
          if (ownParams.isNotEmpty) {
            ownCodeBlock.add(Code("final match = ${fieldName}RegExp.firstMatch(entity.sku);"));
            for (var idx = 0; idx < ownParams.length; ++idx) {
              ownCodeBlock.add(Code(
                  "${idx == 0 ? 'return' : ''} match?.group(${idx + 1}) == ${ownParams[idx].name} ${idx < ownParams.length - 1 ? "&&" : ";"}"));
            }
          } else {
            ownCodeBlock.add(Code("return $fieldName.sku == entity.sku;"));
          }
          ownCodeBlock.add(const Code("}"));
          ownCodeBlock.add(const Code("return false;"));

          methodList.add(Method((method) => method
            ..name = "isOwn${capitalize(fieldName)}"
            ..returns = refer('bool')
            ..body = Block.of(ownCodeBlock)
            ..requiredParameters.add(Parameter((p) => p
              ..type = refer("OrderEntity")
              ..name = 'entity'))
            ..requiredParameters.addAll(ownParams)
            ..static = true));
        }
      }
    }
    for (var entry in capabilitiesIds.entries) {
      final fieldName = buildCapIds(entry.key);
      if (entry.value.isNotEmpty) {
        fieldList.add(Field((field) => field
          ..name = fieldName
          ..static = true
          ..assignment = Code("<ProductId>{${entry.value.join(',')}}")
          ..modifier = FieldModifier.final$));
        iapCapIdsCheckMap[fieldName] = (iapCapIdsCheckMap[fieldName] ??= {})..add(flavor);
      }
    }

    for (var group in groups) {
      final fieldName = camelCase("${group}_group");

      final commonIds = groupSubsIds[group] ?? {};

      fieldList.add(Field((field) => field
        ..name = fieldName
        ..static = true
        ..assignment = Code("<ProductId>{${commonIds.join(',')}}")
        ..modifier = FieldModifier.final$));
      iapCapIdsCheckMap[fieldName] = (iapCapIdsCheckMap[fieldName] ??= {})..add(flavor);
    }

    final groupSkuPairs = <String>[];
    for (var groupEntry in groupSubsIds.entries) {
      final group = groupEntry.key;
      final ids = groupEntry.value;
      for (var id in ids) {
        groupSkuPairs.add("$id.sku : '$group'");
      }
    }
    fieldList.add(Field((field) => field
      ..name = "groupMap"
      ..static = true
      ..assignment = Code("<String, String>{${groupSkuPairs.join(',')}}")
      ..modifier = FieldModifier.final$));

    fieldList.add(Field((field) => field
      ..name = "oneOffChargeIapIds"
      ..static = true
      ..assignment = Code("<ProductId>{${oneOffChargeIapIds.join(',')}}")
      ..modifier = FieldModifier.final$));

    fieldList.add(Field((field) => field
      ..name = "subscriptionsIapIds"
      ..static = true
      ..assignment = Code("<ProductId>{${subscriptionsIapIds.join(',')}}")
      ..modifier = FieldModifier.final$));

    fieldList.add(Field((field) => field
      ..name = "pointsIapIds"
      ..static = true
      ..assignment = Code("<ProductId>{${pointsIapIds.join(',')}}")
      ..modifier = FieldModifier.final$));

    fieldList.add(Field((field) => field
      ..name = "rewardIds"
      ..static = true
      ..assignment = Code("<ProductId>{${rewardIds.join(',')}}")
      ..modifier = FieldModifier.final$));

    fieldList.add(Field((field) => field
      ..name = "igcIds"
      ..static = true
      ..assignment = Code("<ProductId>{${igcIds.join(',')}}")
      ..modifier = FieldModifier.final$));

    fieldList.add(Field((field) => field
      ..name = "manifestBuilders"
      ..static = true
      ..assignment = Code("<ManifestBuilder>[${manifestBuilders.join(',')}]")
      ..modifier = FieldModifier.constant));

    return [
      Class((clazz) {
        clazz
          ..name = '_${capitalize(flavor)}Products'
          ..fields.addAll([...regExpField, ...fieldList])
          ..methods.addAll([...methodList, ...buildIapProfileMethods()]);
      }),
    ];
  }

  String toSetString(String str) {
    final set = str.split(",");
    return set.map((e) => "'$e'").toSet().toString();
  }

  Class buildExperiments(Map<String, dynamic> map, {String flavor = ""}) {
    final List<Field> fieldList = <Field>[];
    final List<Code> experimentsCode = <Code>[const Code("<String, ABTestExperiment>{")];
    for (var fieldEntry in map.entries) {
      final experimentName = fieldEntry.key;
      final experimentData = fieldEntry.value;
      final start = experimentData['start'];
      final end = experimentData['end'];
      final audience = experimentData['audience'];

      final startDt = DateTime.parse(start);
      final endDt = DateTime.parse(end);

      final codeBlocks = <Code>[const Code("ABTestExperiment(")];
      codeBlocks.add(Code("name: '$experimentName',"));
      codeBlocks.add(Code("startTs: ${startDt.millisecondsSinceEpoch}, // $startDt"));
      codeBlocks.add(Code("endTs: ${endDt.millisecondsSinceEpoch}, // $endDt"));

      log.info(
          "===> start:$start(${DateTime.parse(start).millisecondsSinceEpoch})  end:$end(${DateTime.parse(end)})");
      final audienceCodeBlocks = <Code>[const Code("audience: ABTestAudience(")];
      audienceCodeBlocks.add(const Code("filters: ["));
      final filters = audience['filters'] as List;
      for (var filter in filters) {
        log.info("filter:$filter");
        final version = filter['version'];
        if (version != null && version is Map) {
          final opt = version['opt'];
          final mmp = version['mmp'];
          if (conditionOpts.containsKey(opt) && mmp != null) {
            audienceCodeBlocks.add(Code("VersionFilter.${conditionOpts[opt]}('$mmp'),"));
          }
        }

        final newUser = filter['new_user'];
        if (newUser.toString() == "true") {
          audienceCodeBlocks.add(const Code("NewUserFilter(),"));
        }

        final country = filter['country'];
        if (country != null && country is Map) {
          final included = country['included'] as String?;
          final excluded = country['excluded'] as String?;
          if (included != null && included.isNotEmpty) {
            audienceCodeBlocks.add(Code("CountryFilter.included(${toSetString(included)}),"));
          } else if (excluded != null && excluded.isNotEmpty) {
            audienceCodeBlocks.add(Code("CountryFilter.excluded(${toSetString(excluded)}),"));
          }
        }

        final platform = filter['platform'];
        if (platform != null && platform is Map) {
          audienceCodeBlocks.add(const Code("PlatformFilter("));

          final androidCondition = platform["android"];
          if (androidCondition != null && androidCondition is Map) {
            final opt = androidCondition['opt'];
            final sdkInt = androidCondition['ver'];
            if (conditionOpts.containsKey(opt) && sdkInt != null) {
              audienceCodeBlocks.add(Code(
                  "androidCondition: AndroidCondition(opt: ConditionOpt.${conditionOpts[opt]}, sdkInt:$sdkInt),"));
            }
          }

          final iosCondition = platform["ios"];
          if (iosCondition != null && iosCondition is Map) {
            final opt = iosCondition['opt'];
            final version = iosCondition['ver'];
            if (conditionOpts.containsKey(opt) && version != null) {
              audienceCodeBlocks.add(Code(
                  "iosCondition: IosCondition(opt: ConditionOpt.${conditionOpts[opt]}, version:$version),"));
            }
          }
          audienceCodeBlocks.add(const Code("),"));
        }
      }
      audienceCodeBlocks.add(const Code("],"));
      audienceCodeBlocks.add(Code("variant: ${audience['variant']})"));

      codeBlocks.addAll(audienceCodeBlocks);
      codeBlocks.add(const Code(")"));

      final fieldName = camelCase(experimentName);
      if (fieldName != '') {
        fieldList.add(Field(
          (field) => field
            ..name = fieldName
            ..static = true
            ..assignment = Block.of(codeBlocks)
            ..modifier = FieldModifier.final$,
        ));
        // codeBlocks.add(Code("$fieldName: '$fieldData',"));
        experimentsCode.add(Code("'$experimentName': $fieldName,"));
      }
    }
    experimentsCode.add(const Code("}"));

    fieldList.add(Field(
      (field) => field
        ..name = "experiments"
        ..static = true
        ..assignment = Block.of(experimentsCode)
        ..modifier = FieldModifier.final$,
    ));

    return Class((clazz) {
      clazz
        ..name = '_${capitalize(flavor, useCamelCase: true)}ABTestExperiments'
        ..fields.addAll(fieldList);
    });
  }

  static final Map<String, Set<String>> validateSameCategoryManifestMap = {};

  Method _buildProductManifestMethod(String flavor, String productName, String category, String sku,
      bool hasOfferSubs, List<String> skuParamsList, Map<String, dynamic> map) {
    final List<Code> codeBlocks = [];
    final List<Code> extrasBlocks = [];
    final List<Code> detailsBlocks = [];
    final Set<String> productValidate = {};
    if (paramsExp.hasMatch(sku)) {
      codeBlocks
          .add(Code("final matches = ${productName}RegExp.allMatches(intent.productId.sku);"));
      codeBlocks.add(const Code("if (matches.isEmpty) {"));
      codeBlocks.add(const Code("return null;"));
      codeBlocks.add(const Code("}"));
    } else {
      if (hasOfferSubs) {
        final offerIdsField = "${productName}OfferIds";
        codeBlocks.add(Code("if (!$offerIdsField.contains(intent.productId)) {"));
      } else {
        codeBlocks.add(Code("if (intent.productId != $productName) {"));
      }
      codeBlocks.add(const Code("return null;"));
      codeBlocks.add(const Code("}"));
    }
    detailsBlocks.add(const Code("final details = <Details>[];"));
    extrasBlocks.add(const Code("final extras = <String, dynamic> {"));
    extrasBlocks.add(const Code("ExtraReservedField.contentId: intent.productId.sku,"));
    extrasBlocks.add(const Code("ExtraReservedField.scene: intent.scene,"));
    extrasBlocks.add(const Code("ExtraReservedField.rate: intent.rate,"));
    extrasBlocks.add(const Code("ExtraReservedField.sales: intent.sales,"));

    for (var data in map.entries) {
      if (detailsExp.hasMatch(data.key)) {
        final details = data.value as Map<String, dynamic>;
        final type = details.remove("type");
        String sku = details.remove("sku") ?? type;
        final amount = details.remove("amount");
        if (type == null || amount == null) {
          const todo = 'Please define `type` or `amount` field to guru_spec.yaml.';
          throw InvalidGenerationSourceError(
            'Generator cannot target `$productName`.\n[TODO] $todo',
            todo: todo,
          );
        }
        final ignoreSales = details.remove("ignore_sales");
        if (ignoreSales != null && ignoreSales is! bool) {
          final todo =
              '[$productName] ignoreSales:$ignoreSales ${ignoreSales.runtimeType} field is not `bool`';
          throw InvalidGenerationSourceError(
            'Generator cannot target `$productName`.\n[TODO] $todo',
            todo: todo,
          );
        }

        final filledMatches = fillParamsExp.allMatches(sku).toList();
        if (filledMatches.isNotEmpty) {
          for (var idx = 0; idx < filledMatches.length; ++idx) {
            sku = sku.replaceAllMapped(fillParamsExp, (match) {
              return  "\${matches.first.group(${match.group(1)})!}";
            });
          }
        }

        if (ignoreSales == true) {
          detailsBlocks.add(Code("details.add(Details.define('$type', $amount, sku: '$sku')"));
        } else {
          detailsBlocks.add(Code(
              "details.add(Details.define('$type', intent.sales ? max($amount, (intent.rate * $amount).toInt()) : $amount, sku: '$sku')"));
        }
        for (var detail in details.entries) {
          final fieldData = detail.value;
          detailsBlocks.add(Code("..${buildSetParams(detail.key, fieldData, skuParamsList)}"));
        }
        detailsBlocks.add(const Code(");"));
      } else {
        final pairCode = buildParamsPair(data.key, data.value, skuParamsList);
        log.info("data.key:${data.key} data.value:${data.value} = $pairCode");
        productValidate.add(data.key);
        if (pairCode != null) {
          extrasBlocks.add(Code("$pairCode,"));
        }
      }
    }

    final validateData = (validateSameCategoryManifestMap[productName] ??= productValidate.toSet());
    final checkedData = validateData.toSet()..removeAll(productValidate);
    final checkedData2 = productValidate.toSet()..removeAll(validateData);

    if (checkedData.isNotEmpty || checkedData2.isNotEmpty) {
      final todo =
          '[$flavor]The manifests of the same `$productName` category have mismatched parameters. Please check if the manifest in the `$productName` matches ${validateData..removeAll(skuParamsList)}.';
      throw InvalidGenerationSourceError(
        'Generator cannot target `$productName`.\n[TODO] $todo',
        todo: todo,
      );
    }

    final List<String> manifestParams = ["'$category'"];
    // bool isConst = false;
    if (extrasBlocks.length > 1) {
      extrasBlocks.add(const Code("};"));
      codeBlocks.addAll(extrasBlocks);
      manifestParams.add("extras: extras");
      // isConst = false;
    }

    if (hasOfferSubs) {
      codeBlocks.add(const Code("if (Platform.isAndroid && intent.productId.hasOffer) {"));
      codeBlocks
          .add(const Code("extras[ExtraReservedField.basePlanId] = intent.productId.basePlan;"));
      codeBlocks.add(const Code("extras[ExtraReservedField.offerId] = intent.productId.offerId;"));
      codeBlocks.add(const Code("}"));
    }

    if (detailsBlocks.length > 1) {
      codeBlocks.addAll(detailsBlocks);
      manifestParams.add("details: details");
      // isConst = false;
    }

    codeBlocks.add(Code("return Manifest(${manifestParams.join(", ")});"));

    return Method((method) => method
      ..name = "build${capitalize(productName)}Manifest"
      ..returns = refer('Future<Manifest?>')
      ..body = Block.of(codeBlocks)
      ..modifier = MethodModifier.async
      ..requiredParameters.add(Parameter((p) => p
        ..type = refer("TransactionIntent")
        ..name = 'intent'))
      ..static = true);
  }

  String? buildSetParams(String fieldName, dynamic fieldData, List<String> skuParamsList) {
    if (fieldData is int) {
      return "setInt('$fieldName', $fieldData)";
    } else if (fieldData is double) {
      return "setDouble('$fieldName', $fieldData})";
    } else if (fieldData is bool) {
      return "setBool('$fieldName', ${fieldData ? "true" : "false"})";
    } else {
      final filledMatches = fillParamsExp.allMatches(fieldData).toList();
      if (filledMatches.isNotEmpty) {
        for (var idx = 0; idx < filledMatches.length; ++idx) {
          final match = filledMatches[idx];
          return "setString('$fieldName', matches.first.group(${match.group(1)})!)";
        }
      } else {
        final matches = typeParamsExp.allMatches(fieldData);
        if (matches.isNotEmpty) {
          final fieldName = matches.first.group(1);
          final fieldType = matches.first.group(2)?.toLowerCase();
          if (fieldType == 'int') {
            return "setInt('$fieldName', $fieldName)";
          } else if (fieldData == "double") {
            return "setDouble('$fieldName', $fieldName})";
          } else if (fieldData == "bool") {
            return "setBool('$fieldName', $fieldName)";
          } else {
            return "setString('$fieldName', $fieldData)";
          }
        } else {
          return "setString('$fieldName', '$fieldData')";
        }
      }
    }
    return null;
  }

  static const _manifestExtraReservedMap = {
    "icon": "ExtraReservedField.icon",
    "name": "ExtraReservedField.name"
  };

  String? buildParamsPair(String fieldName, dynamic fieldData, List<String> skuParamsList) {
    final reservedExtraKey = _manifestExtraReservedMap[fieldName.toLowerCase()];
    if (reservedExtraKey != null) {
      // if (fieldData !is String) {
      //   final todo = 'fieldName `$fieldName` is Reserved field! it need `String` type!! Please check it!!';
      //   throw InvalidGenerationSourceError(
      //     'Generator cannot target buildParamsPair `$fieldName`.\n[TODO] $todo',
      //     todo: todo,
      //   );
      // }
      // if (fieldName.toLowerCase() == "scene") {
      final todo = 'fieldName `$fieldName` is reserved field! Cannot be defined by guru_spec';
      throw InvalidGenerationSourceError(
        'Generator cannot target buildParamsPair `$fieldName`.\n[TODO] $todo',
        todo: todo,
      );
      // }
      return "$reservedExtraKey : $fieldData";
    }
    if (fieldData is int) {
      return "'$fieldName': $fieldData";
    } else if (fieldData is double) {
      return "'$fieldName': $fieldData";
    } else if (fieldData is bool) {
      return "'$fieldName': ${fieldData ? "true" : "false"}";
    } else {
      final filledMatches = fillParamsExp.allMatches(fieldData).toList();
      if (filledMatches.isNotEmpty) {
        for (var idx = 0; idx < filledMatches.length; ++idx) {
          final match = filledMatches[idx];
          final paramsIdx = int.parse(match.group(1)!);
          return "'$fieldName': matches.first.group($paramsIdx)!";
        }
      } else {
        final matches = typeParamsExp.allMatches(fieldData);
        if (matches.isNotEmpty) {
          final fieldName = matches.first.group(1);
          final fieldType = matches.first.group(2)?.toLowerCase();
          if (fieldType == 'int') {
            return "'$fieldName': $fieldName";
          } else if (fieldData == "double") {
            return "'$fieldName': $fieldName";
          } else if (fieldData == "bool") {
            return "'$fieldName': $fieldName";
          } else {
            return "'$fieldName': $fieldData";
          }
        } else {
          return "'$fieldName': '$fieldData'";
        }
      }
    }
    return null;
  }

  // Class _buildProductManifest(String flavor, Map<String, dynamic> map) {
  //   final methodList = <Method>[];
  //   final fieldList = <Field>[];
  //   for (var product in map.entries) {
  //     final manifestName = product.key;
  //     final manifestData = product.value;
  //
  //     final sku = (manifestData.remove("sku") ?? '') as String;
  //     if (sku.isNotEmpty != true) {
  //       final todo = 'Please define `sku` field to [$manifestName] in guru_spec.yaml.';
  //       throw InvalidGenerationSourceError(
  //         'Generator cannot target `$manifestName`.\n[TODO] $todo',
  //         todo: todo,
  //       );
  //     }
  //
  //     final category = (manifestData.remove("category") ?? '') as String;
  //     if (category.isNotEmpty != true) {
  //       final todo = 'Please define `category` field to [$manifestName] in guru_spec.yaml.';
  //       throw InvalidGenerationSourceError(
  //         'Generator cannot target `$manifestName`.\n[TODO] $todo',
  //         todo: todo,
  //       );
  //     }
  //
  //     fieldList.add(Field((field) => field
  //       ..name = "${capitalize(manifestName, useCamelCase: true)}Matcher"
  //       ..static = true
  //       ..assignment = Code("RegExp($sku)")
  //       ..modifier = FieldModifier.final$));
  //
  //     final List<Code> codeBlocks = [];
  //     codeBlocks.add(const Code("final details = <Details>[];"));
  //     for (var data in manifestData.entries) {
  //       if (detailsExp.hasMatch(data.key)) {
  //         final details = data.value as Map<String, dynamic>;
  //         final type = details.remove("type");
  //         final amount = details.remove("amount");
  //         if (type == null || amount == null) {
  //           const todo = 'Please define `type` or `amount` field to guru_spec.yaml.';
  //           throw InvalidGenerationSourceError(
  //             'Generator cannot target `$manifestName`.\n[TODO] $todo',
  //             todo: todo,
  //           );
  //         }
  //         final ignoreSales = details.remove("ignore_sales");
  //         if (ignoreSales != null && ignoreSales is! bool) {
  //           final todo =
  //               '[$manifestName] ignoreSales:$ignoreSales ${ignoreSales.runtimeType} field is not `bool`';
  //           throw InvalidGenerationSourceError(
  //             'Generator cannot target `$manifestName`.\n[TODO] $todo',
  //             todo: todo,
  //           );
  //         }
  //
  //         if (ignoreSales == true) {
  //           codeBlocks.add(Code("details.add(Details.define('$type', $amount)"));
  //         } else {
  //           codeBlocks.add(Code(
  //               "details.add(Details.define('$type', intent.sales ? max($amount, (intent.rate * $amount).toInt()) : $amount)"));
  //         }
  //         for (var detail in details.entries) {
  //           final fieldData = detail.value;
  //           if (fieldData is int) {
  //             codeBlocks.add(Code("..setInt('${detail.key}', $fieldData)"));
  //           } else if (fieldData is double) {
  //             codeBlocks.add(Code("..setDouble('${detail.key}', $fieldData})"));
  //           } else if (fieldData is bool) {
  //             codeBlocks.add(Code("..setBool('${detail.key}', ${fieldData ? "true" : "false"})"));
  //           } else {
  //             final matches = typeParamsExp.allMatches(fieldData);
  //             if (matches.isNotEmpty) {
  //               final fieldName = matches.first.group(1);
  //               final fieldType = matches.first.group(2)?.toLowerCase();
  //               if (fieldType == 'int') {
  //                 codeBlocks.add(Code("..setInt('${detail.key}', $fieldName)"));
  //               } else if (fieldData == "double") {
  //                 codeBlocks.add(Code("..setDouble('${detail.key}', $fieldName})"));
  //               } else if (fieldData == "bool") {
  //                 codeBlocks.add(Code("..setBool('${detail.key}', $fieldName)"));
  //               } else {
  //                 codeBlocks.add(Code("..setString('${detail.key}', $fieldData)"));
  //               }
  //             } else {
  //               codeBlocks.add(Code("..setString('${detail.key}', '$fieldData')"));
  //             }
  //           }
  //         }
  //         codeBlocks.add(const Code(");"));
  //       }
  //     }
  //     codeBlocks.add(Code("return Manifest('$category', details: details);"));
  //
  //     methodList.add(Method((method) => method
  //       ..name = "build${capitalize(manifestName)}Manifest"
  //       ..returns = refer('Manifest')
  //       ..body = Block.of(codeBlocks)
  //       ..requiredParameters.add(Parameter((p) => p
  //         ..type = refer("TransactionIntent")
  //         ..name = 'intent'))
  //       ..static = true));
  //   }
  //   return Class((clazz) {
  //     clazz
  //       ..name = '_${capitalize(flavor)}ProductManifest'
  //       ..fields.addAll(fieldList)
  //       ..methods.addAll(methodList);
  //     // ..methods.addAll([_buildCreator(element, blocks)])
  //     // ..methods.addAll(designSpec.nestedSpec ? [] : [_buildGetter(element)])
  //     // ..extend = refer(className);
  //   });
  //   return Class((clazz) => clazz..name = "Hello");
  // }

  static final RegExp detailsExp = RegExp(r"details\d*$");
  static final RegExp paramsExp = RegExp(r"\{([^}]+)\}");
  static final RegExp typeParamsExp = RegExp(r"\{([^}]+)\}:(string|int|double|bool)$");
  static final RegExp fillParamsExp = RegExp(r"\{(\d)\}");

  // List<Field> _buildProductField(Map<String, dynamic> map, String flavor) {
  //   final List<Field> fieldList = <Field>[];
  //   final List<Method> methodList = <Method>[];
  //   // String? noAds;
  //   final List<String> oneOffChargeIapIds = [];
  //   final List<String> subscriptionsIapIds = [];
  //   final List<String> rewardIds = [];
  //   final List<String> igcIds = [];
  //   final Map<String, Set<String>> capabilitiesIds = {};
  //   for (var fieldEntry in map.entries) {
  //     String fieldName = camelCase(fieldEntry.key);
  //     final fieldData = fieldEntry.value;
  //     if (fieldName != '' && fieldData != null) {
  //       final attr = fieldData['attr'] ?? '';
  //       final capabilities = (fieldData["capabilities"] ?? '') as String;
  //       final android = fieldData['android'] ?? '';
  //       final ios = fieldData['ios'] ?? '';
  //       final sku = (fieldData['sku'] ?? '') as String;
  //       final methods = ((fieldData['method'] ?? '') as String).split(",");
  //       for (var method in methods) {
  //         if (method == 'iap') {
  //           if (attr == 'subscriptions') {
  //             subscriptionsIapIds.add(fieldName);
  //           } else {
  //             oneOffChargeIapIds.add(fieldName);
  //           }
  //         } else if (method == 'reward') {
  //           rewardIds.add(fieldName);
  //         } else if (method == 'igc') {
  //           igcIds.add(fieldName);
  //         }
  //       }
  //       if (capabilities != '') {
  //         final segments = capabilities.split('|');
  //         final capabilitiesList =
  //             segments.where((segment) => productIdsCapabilities.contains(segment)).toList();
  //         for (var capability in capabilitiesList) {
  //           capabilitiesIds[capability] = (capabilitiesIds[capability] ??= <String>{})
  //             ..add(fieldName);
  //         }
  //       }
  //
  //       if (paramsExp.hasMatch(sku)) {
  //         final List<Parameter> params = [];
  //         final str = sku.replaceAllMapped(paramsExp, (match) {
  //           final param = match.group(1) ?? "";
  //           params.add(Parameter((p) => p
  //             ..type = refer("String")
  //             ..name = param));
  //           return "\${${match.group(1)}}";
  //         });
  //
  //         methodList.add(Method((method) => method
  //           ..name = fieldName
  //           ..returns = refer('ProductId')
  //           ..lambda = true
  //           ..optionalParameters.addAll(params)
  //           ..body = Code(str)
  //           ..static = true));
  //       } else {
  //         fieldList.add(Field((field) => field
  //           ..name = fieldName
  //           ..static = true
  //           ..assignment = sku.isNotEmpty
  //               ? Code("ProductId.fromSku(sku: '$sku', attr: TransactionAttributes.$attr)")
  //               : Code(
  //                   "ProductId(android: '$android', ios: '$ios', attr: TransactionAttributes.$attr)")
  //           ..modifier = FieldModifier.constant));
  //       }
  //
  //       iapIdsCheckMap[fieldName] = (iapIdsCheckMap[fieldName] ??= {})..add(flavor);
  //     }
  //   }
  //
  //   List<Method> buildIapProfileMethods() {
  //     final List<Method> methodList = <Method>[];
  //     methodList.add(Method((method) => method
  //       ..name = 'ids'
  //       ..type = MethodType.getter
  //       ..returns = refer('List<ProductId>')
  //       ..lambda = true
  //       ..body = const Code("[...oneOffChargeIapIds, ...subscriptionsIapIds]")
  //       ..static = true));
  //     return methodList;
  //   }
  //
  //   for (var entry in capabilitiesIds.entries) {
  //     final fieldName = buildCapIds(entry.key);
  //     if (entry.value.isNotEmpty) {
  //       fieldList.add(Field((field) => field
  //         ..name = fieldName
  //         ..static = true
  //         ..assignment = Code("<ProductId>[${entry.value.join(',')}]")
  //         ..modifier = FieldModifier.constant));
  //       iapCapIdsCheckMap[fieldName] = (iapCapIdsCheckMap[fieldName] ??= {})..add(flavor);
  //     }
  //   }
  //
  //   fieldList.add(Field((field) => field
  //     ..name = "oneOffChargeIapIds"
  //     ..static = true
  //     ..assignment = Code("<ProductId>[${oneOffChargeIapIds.join(',')}]")
  //     ..modifier = FieldModifier.constant));
  //
  //   fieldList.add(Field((field) => field
  //     ..name = "subscriptionsIapIds"
  //     ..static = true
  //     ..assignment = Code("<ProductId>[${subscriptionsIapIds.join(',')}]")
  //     ..modifier = FieldModifier.constant));
  //
  //   fieldList.add(Field((field) => field
  //     ..name = "rewardIds"
  //     ..static = true
  //     ..assignment = Code("<ProductId>[${rewardIds.join(',')}]")
  //     ..modifier = FieldModifier.constant));
  //
  //   fieldList.add(Field((field) => field
  //     ..name = "igcIds"
  //     ..static = true
  //     ..assignment = Code("<ProductId>[${igcIds.join(',')}]")
  //     ..modifier = FieldModifier.constant));
  //   return fieldList;
  // }

  // Class buildIapProductIds(Map<String, dynamic> map, {String flavor = ""}) {
  //   return Class((clazz) {
  //     clazz
  //       ..name = '_${capitalize(flavor)}IapProductIds'
  //       ..fields.addAll(_buildIapProductField(map, flavor))
  //       ..methods.addAll(buildIapProfileMethods());
  //     // ..methods.addAll([_buildCreator(element, blocks)])
  //     // ..methods.addAll(designSpec.nestedSpec ? [] : [_buildGetter(element)])
  //     // ..extend = refer(className);
  //   });
  // }

  Class buildRemoteConfigConstantsClass(Map<String, dynamic> map, {String flavor = ""}) {
    final List<Field> fieldList = <Field>[];
    final codeBlocks = <Code>[const Code("{")];
    final mapping = map.remove("_mapping");
    Code getKeyBody = const Code("key");

    for (var fieldEntry in map.entries) {
      final fieldName = camelCase(fieldEntry.key);
      final fieldData = fieldEntry.value;
      if (fieldName != '' && fieldData != null) {
        fieldList.add(Field((field) => field
          ..name = fieldName
          ..static = true
          ..assignment = Code("'${fieldEntry.key}'")
          ..modifier = FieldModifier.constant));
        codeBlocks.add(Code("$fieldName: '$fieldData',"));
        remoteConfigKeys[fieldEntry.key] = fieldName;
      }
    }

    codeBlocks.add(const Code("}"));

    fieldList.add(Field((field) => field
      ..name = '_defaultConfigs'
      ..static = true
      ..type = refer("Map<String, dynamic>")
      ..assignment = Block.of(codeBlocks)
      ..modifier = FieldModifier.constant));

    if (mapping != null && mapping is Map) {
      final mappingBlocks = <Code>[const Code("{")];
      for (var fieldEntry in mapping.entries) {
        final fieldName = fieldEntry.key;
        final fieldData = fieldEntry.value;
        if (fieldName != '' && fieldData != null) {
          mappingBlocks.add(Code("'$fieldName': '$fieldData',"));
        }
      }
      mappingBlocks.add(const Code("}"));

      fieldList.add(Field((field) => field
        ..name = "_mapping"
        ..static = true
        ..type = refer("Map<String, String>")
        ..modifier = FieldModifier.constant
        ..assignment = Block.of(mappingBlocks)));
      getKeyBody = const Code("_mapping[key] ?? key");
    }

    final List<Method> methodList = <Method>[];
    methodList.add(Method((method) => method
      ..name = 'getDefaultConfigString'
      ..returns = refer('String')
      ..lambda = true
      ..requiredParameters.addAll([
        Parameter((p) => p
          ..type = refer("String")
          ..name = "key")
      ])
      ..body = const Code("_defaultConfigs[key]")
      ..static = true));

    methodList.add(Method((method) => method
      ..name = 'getKey'
      ..returns = refer('String')
      ..lambda = true
      ..requiredParameters.addAll([
        Parameter((p) => p
          ..type = refer("String")
          ..name = "key")
      ])
      ..body = getKeyBody
      ..static = true));

    return Class((clazz) {
      clazz
        ..name = '_${capitalize(flavor)}RemoteConfigConstants'
        ..fields.addAll(fieldList)
        ..methods.addAll(methodList);
    });
  }

// => Field((field) => field

}
