package com.blackduck.integration.detect.configuration;

import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.stream.Collectors;

import com.blackduck.integration.blackduck.api.generated.enumeration.PolicyRuleSeverityType;
import com.blackduck.integration.blackduck.api.generated.enumeration.ProjectCloneCategoriesType;
import com.blackduck.integration.blackduck.api.generated.enumeration.ProjectVersionDistributionType;
import com.blackduck.integration.blackduck.api.manual.temporary.enumeration.ProjectVersionPhaseType;
import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.IndividualFileMatching;
import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.ReducedPersistence;
import com.blackduck.integration.blackduck.codelocation.signaturescanner.command.SnippetMatching;
import com.blackduck.integration.configuration.property.Properties;
import com.blackduck.integration.configuration.property.Property;
import com.blackduck.integration.configuration.property.base.PassthroughProperty;
import com.blackduck.integration.configuration.property.base.TypedProperty;
import com.blackduck.integration.configuration.property.types.bool.BooleanProperty;
import com.blackduck.integration.configuration.property.types.enumallnone.enumeration.AllEnum;
import com.blackduck.integration.configuration.property.types.enumallnone.enumeration.AllNoneEnum;
import com.blackduck.integration.configuration.property.types.enumallnone.enumeration.NoneEnum;
import com.blackduck.integration.configuration.property.types.enumallnone.property.AllEnumListProperty;
import com.blackduck.integration.configuration.property.types.enumallnone.property.AllNoneEnumListProperty;
import com.blackduck.integration.configuration.property.types.enumallnone.property.NoneEnumListProperty;
import com.blackduck.integration.configuration.property.types.enumextended.ExtendedEnumProperty;
import com.blackduck.integration.configuration.property.types.enumextended.ExtendedEnumValue;
import com.blackduck.integration.configuration.property.types.enums.EnumListProperty;
import com.blackduck.integration.configuration.property.types.enums.EnumProperty;
import com.blackduck.integration.configuration.property.types.integer.IntegerProperty;
import com.blackduck.integration.configuration.property.types.integer.NullableIntegerProperty;
import com.blackduck.integration.configuration.property.types.longs.LongProperty;
import com.blackduck.integration.configuration.property.types.path.NullablePathProperty;
import com.blackduck.integration.configuration.property.types.path.PathListProperty;
import com.blackduck.integration.configuration.property.types.string.CaseSensitiveStringListProperty;
import com.blackduck.integration.configuration.property.types.string.NullableStringProperty;
import com.blackduck.integration.configuration.property.types.string.StringListProperty;
import com.blackduck.integration.configuration.property.types.string.StringProperty;
import com.blackduck.integration.detect.configuration.enumeration.BlackduckScanMode;
import com.blackduck.integration.detect.configuration.enumeration.DetectCategory;
import com.blackduck.integration.detect.configuration.enumeration.DetectGroup;
import com.blackduck.integration.detect.configuration.enumeration.DetectTargetType;
import com.blackduck.integration.detect.configuration.enumeration.DetectTool;
import com.blackduck.integration.detect.configuration.enumeration.RapidCompareMode;
import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedIndividualFileMatchingMode;
import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedReducedPersistanceMode;
import com.blackduck.integration.detect.tool.signaturescanner.enums.ExtendedSnippetMode;
import com.blackduck.integration.detectable.detectables.bazel.WorkspaceRule;
import com.blackduck.integration.detectable.detectables.bitbake.BitbakeDependencyType;
import com.blackduck.integration.detectable.detectables.cargo.CargoDependencyType;
import com.blackduck.integration.detectable.detectables.conan.cli.config.ConanDependencyType;
import com.blackduck.integration.detectable.detectables.dart.pubdep.DartPubDependencyType;
import com.blackduck.integration.detectable.detectables.go.gomod.GoModDependencyType;
import com.blackduck.integration.detectable.detectables.gradle.inspection.GradleConfigurationType;
import com.blackduck.integration.detectable.detectables.lerna.LernaPackageType;
import com.blackduck.integration.detectable.detectables.npm.NpmDependencyType;
import com.blackduck.integration.detectable.detectables.nuget.NugetDependencyType;
import com.blackduck.integration.detectable.detectables.packagist.PackagistDependencyType;
import com.blackduck.integration.detectable.detectables.pear.PearDependencyType;
import com.blackduck.integration.detectable.detectables.pipenv.parse.PipenvDependencyType;
import com.blackduck.integration.detectable.detectables.pnpm.lockfile.model.PnpmDependencyType;
import com.blackduck.integration.detectable.detectables.rubygems.GemspecDependencyType;
import com.blackduck.integration.detectable.detectables.yarn.YarnDependencyType;
import com.blackduck.integration.detector.base.DetectorType;
import com.blackduck.integration.log.LogLevel;

// java:S1192: Sonar wants constants defined for fromVersion when setting property info.
// java:S1123: Warning about deprecations not having Java doc.
@SuppressWarnings({ "java:S1123", "java:S1192" })
public class DetectProperties {
    private DetectProperties() {
    }

    public static final NullableStringProperty BLACKDUCK_API_TOKEN =
        NullableStringProperty.newBuilder("blackduck.api.token")
            .setInfo("Black Duck SCA API Token", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("The access token used to authenticate with the Black Duck SCA Server.")
            .setGroups(DetectGroup.BLACKDUCK_SERVER, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .build();

    public static final BooleanProperty BLACKDUCK_OFFLINE_MODE =
        BooleanProperty.newBuilder("blackduck.offline.mode", false)
            .setInfo("Offline Mode", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp(
                "This can disable Black Duck SCA communication - if set to true, Detect will not upload BDIO files, or check policies, and it will not download and install the signature scanner. Note that the path to a local instance of the scanner can be provided using the -detect.blackduck.signature.scanner.local.path parameter.")
            .setGroups(DetectGroup.BLACKDUCK_SERVER, DetectGroup.BLACKDUCK, DetectGroup.OFFLINE, DetectGroup.DEFAULT)
            .build();

    public static final NullableStringProperty BLACKDUCK_PROXY_HOST =
        NullableStringProperty.newBuilder("blackduck.proxy.host")
            .setInfo("Proxy Host", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp(
			"Hostname of the proxy server.",
			"Schema/protocol is not accepted by this parameter.")
			.setExample("--blackduck.proxy.host=<Proxy_IP/URL>")
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final StringListProperty BLACKDUCK_PROXY_IGNORED_HOSTS =
        StringListProperty.newBuilder("blackduck.proxy.ignored.hosts", emptyList())
            .setInfo("Bypass Proxy Hosts", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp(
                "A comma separated list of regular expression host patterns that should not use the proxy.",
                "This property accepts Java regular expressions. For further information refer to <xref href=\"https://documentation%2Eblackduck%2Ecom/bundle/detect/page/configuring/javaregex%2Ehtml\" scope=\"external\" outputclass=\"external\" format=\"html\" target=\"_blank\">Java regular expression support page.</xref>"
            )
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .setExample("blackduck[0-9]+.mycompany.com")
            .build();

    public static final NullableStringProperty BLACKDUCK_PROXY_NTLM_DOMAIN =
        NullableStringProperty.newBuilder("blackduck.proxy.ntlm.domain")
            .setInfo("NTLM Proxy Domain", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("NTLM Proxy domain.")
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullableStringProperty BLACKDUCK_PROXY_NTLM_WORKSTATION =
        NullableStringProperty.newBuilder("blackduck.proxy.ntlm.workstation")
            .setInfo("NTLM Proxy Workstation", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("NTLM Proxy workstation.")
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullableStringProperty BLACKDUCK_PROXY_PASSWORD =
        NullableStringProperty.newBuilder("blackduck.proxy.password")
            .setInfo("Proxy Password", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("Proxy password.")
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullableStringProperty BLACKDUCK_PROXY_PORT =
        NullableStringProperty.newBuilder("blackduck.proxy.port")
            .setInfo("Proxy Port", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("Proxy port number.")
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullableStringProperty BLACKDUCK_PROXY_USERNAME =
        NullableStringProperty.newBuilder("blackduck.proxy.username")
            .setInfo("Proxy Username", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("Proxy username.")
            .setGroups(DetectGroup.PROXY, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final BooleanProperty BLACKDUCK_TRUST_CERT =
        BooleanProperty.newBuilder("blackduck.trust.cert", false)
            .setInfo("Trust All SSL Certificates", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("If true, automatically trust the certificate for the current run of Detect only.")
            .setGroups(DetectGroup.BLACKDUCK_SERVER, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullableStringProperty BLACKDUCK_URL =
        NullableStringProperty.newBuilder("blackduck.url")
            .setInfo("Black Duck SCA URL", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp("URL of the Black Duck SCA server.")
            .setExample("https://blackduck.mydomain.com")
            .setGroups(DetectGroup.BLACKDUCK_SERVER, DetectGroup.BLACKDUCK, DetectGroup.DEFAULT)
            .build();

    public static final AllNoneEnumListProperty<DetectorType> DETECT_ACCURACY_REQUIRED =
        AllNoneEnumListProperty.newBuilder("detect.accuracy.required", AllNoneEnum.ALL, DetectorType.class)
            .setInfo("Detector Accuracy Requirements", DetectPropertyFromVersion.VERSION_7_13_0)
            .setHelp(
                "Detector types from which HIGH accuracy results are required when a detector of that type applies.",
                "The value of this property only affects detector types which apply to the source project. If a detector type applies, and is one of the accuracy-required detector types indicated by the value of this property, low accuracy results for that detector type are treated as a failure.  For further information refer to <xref href=\"https://documentation%2Eblackduck%2Ecom/bundle/detect/page/runningdetect/detectorcascade%2Ehtml\" scope=\"external\" outputclass=\"external\" format=\"html\" target=\"_blank\">Detector search and accuracy.</xref>"
            )
            .setGroups(DetectGroup.DETECTOR, DetectGroup.GLOBAL)
            .setExample("ALL,NONE")
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final BooleanProperty DETECT_AUTONOMOUS_SCAN_ENABLED =
            BooleanProperty.newBuilder("detect.autonomous.scan.enabled", false)
                    .setInfo("Autonomous Scan Enabled", DetectPropertyFromVersion.VERSION_9_8_0)
                    .setHelp("If true, Detect will enable autonomous scanning feature.")
                    .setGroups(DetectGroup.GLOBAL)
                    .setCategory(DetectCategory.Advanced)
                    .build();

    public static final IntegerProperty DETECT_PARALLEL_PROCESSORS =
        IntegerProperty.newBuilder("detect.parallel.processors", 1)
            .setInfo("Detect Parallel Processors", DetectPropertyFromVersion.VERSION_6_0_0)
            .setHelp(
                "The number of threads to run processes in parallel, defaults to 1, but if you specify less than or equal to 0, the number of processors on the machine will be used.")
            .setGroups(DetectGroup.GENERAL, DetectGroup.GLOBAL)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullablePathProperty DETECT_BASH_PATH =
        NullablePathProperty.newBuilder("detect.bash.path")
            .setInfo("Bash Executable", DetectPropertyFromVersion.VERSION_3_0_0)
            .setHelp("Path to the Bash executable.", "If set, Detect will use the given Bash executable instead of searching for one.")
            .setExample("/usr/bin/bash")
            .setGroups(DetectGroup.PATHS, DetectGroup.GLOBAL)
            .build();

    public static final NullablePathProperty DETECT_BAZEL_PATH =
        NullablePathProperty.newBuilder("detect.bazel.path")
            .setInfo("Bazel Executable", DetectPropertyFromVersion.VERSION_5_2_0)
            .setHelp("The path to the Bazel executable.")
            .setExample("$HOME/bin/bazel")
            .setGroups(DetectGroup.BAZEL, DetectGroup.GLOBAL)
            .build();

    public static final NullableStringProperty DETECT_BAZEL_TARGET =
        NullableStringProperty.newBuilder("detect.bazel.target")
            .setInfo("Bazel Target", DetectPropertyFromVersion.VERSION_5_2_0)
            .setHelp("The Bazel target (for example, //foo:foolib) for which dependencies are collected. For Detect to run Bazel, this property must be set.")
            .setGroups(DetectGroup.BAZEL, DetectGroup.SOURCE_SCAN)
            .build();

    public static final StringListProperty DETECT_BAZEL_CQUERY_OPTIONS =
        StringListProperty.newBuilder("detect.bazel.cquery.options", emptyList())
            .setInfo("Bazel cquery additional options", DetectPropertyFromVersion.VERSION_6_1_0)
            .setHelp("A comma-separated list of additional options to pass to the bazel cquery command.")
            .setGroups(DetectGroup.BAZEL, DetectGroup.SOURCE_SCAN)
            .build();

    public static final AllNoneEnumListProperty<WorkspaceRule> DETECT_BAZEL_WORKSPACE_RULES =
        AllNoneEnumListProperty.newBuilder("detect.bazel.workspace.rules", AllNoneEnum.NONE, WorkspaceRule.class)
            .setInfo("Bazel workspace rules", DetectPropertyFromVersion.VERSION_7_12_0)
            .setHelp(
                "By default Detect discovers Bazel dependencies using all of the supported Bazel workspace rules that it finds in the WORKSPACE file. Alternatively you can use this property to specify the list of Bazel workspace rules Detect should use.",
                "Setting this property (or letting it default) to NONE tells Detect to use supported rules that it finds in the WORKSPACE file."
            )
            .setGroups(DetectGroup.BAZEL, DetectGroup.SOURCE_SCAN)
            .build();

    public static final NullablePathProperty DETECT_CONAN_PATH =
        NullablePathProperty.newBuilder("detect.conan.path")
            .setInfo("Conan Executable", DetectPropertyFromVersion.VERSION_6_8_0)
            .setHelp("The path to the conan executable.")
            .setGroups(DetectGroup.CONAN, DetectGroup.SOURCE_SCAN)
            .build();

    public static final NoneEnumListProperty<ConanDependencyType> DETECT_CONAN_DEPENDENCY_TYPES_EXCLUDED =
        NoneEnumListProperty.newBuilder("detect.conan.dependency.types.excluded", NoneEnum.NONE, ConanDependencyType.class)
            .setInfo("Conan Dependency Types Excluded", DetectPropertyFromVersion.VERSION_7_10_0)
            .setHelp(createTypeFilterHelpText("Conan dependency types"))
            .setExample(ConanDependencyType.BUILD.name())
            .setGroups(DetectGroup.CONAN, DetectGroup.SOURCE_SCAN)
            .build();

    public static final NullableStringProperty DETECT_CONAN_ARGUMENTS =
        NullableStringProperty.newBuilder("detect.conan.arguments")
            .setInfo("Additional Conan Arguments", DetectPropertyFromVersion.VERSION_6_8_0)
            .setHelp(
                "A space-separated list of additional arguments to add to the 'conan info' command line when running Detect against a Conan project. Detect will execute the command 'conan info {additional arguments} .'")
            .setGroups(DetectGroup.CONAN, DetectGroup.SOURCE_SCAN)
            .setExample("\"--profile clang --profile cmake_316\"")
            .build();

    public static final NullablePathProperty DETECT_CONAN_LOCKFILE_PATH =
        NullablePathProperty.newBuilder("detect.conan.lockfile.path")
            .setInfo("Conan Lockfile", DetectPropertyFromVersion.VERSION_6_8_0)
            .setHelp(
                "The path to the conan lockfile to apply when running 'conan info' to get the dependency graph.",
                "If set, the value will be used by CLI and lockfile detectors to determine the component versions and/or relationships.'"
            )
            .setGroups(DetectGroup.CONAN, DetectGroup.SOURCE_SCAN)
            .build();

    // TODO: Should the default change? Has KB matching of Conan projects improved? If so, this could change to 'true'.
    public static final BooleanProperty DETECT_CONAN_REQUIRE_PREV_MATCH =
        BooleanProperty.newBuilder("detect.conan.attempt.package.revision.match", false)
            .setInfo(
                "Attempt Package Revision Match",
                DetectPropertyFromVersion.VERSION_6_8_0
            )
            .setHelp(
                "If package revisions are available (a Conan lock file is found or provided, and Conan's revisions feature is enabled), require that each dependency's package revision match the package revision of the component in the KB.")
            .setGroups(DetectGroup.CONAN, DetectGroup.SOURCE_SCAN)
            .build();
    public static final NullablePathProperty DETECT_BDIO_OUTPUT_PATH =
        NullablePathProperty.newBuilder("detect.bdio.output.path")
            .setInfo("BDIO Output Directory", DetectPropertyFromVersion.VERSION_3_0_0)
            .setHelp("The path to the output directory for the generated BDIO file.", "If not set, the BDIO file will be placed in a 'BDIO' subdirectory of the output directory.")
            .setGroups(DetectGroup.PATHS, DetectGroup.GLOBAL)
            .setExample("/home/<username>/blackduck/scan-outputs/bdio")
            .build();

    public static final NullableStringProperty DETECT_BDIO_FILE_NAME =
        NullableStringProperty.newBuilder("detect.bdio.file.name")
            .setInfo("BDIO File Name", DetectPropertyFromVersion.VERSION_7_9_0)
            .setHelp(
                "The desired file name of BDIO file Detect produces in the BDIO Output Directory.",
                "If not set, the file name is generated from your project, version and code location names."
            )
            .setGroups(DetectGroup.PATHS, DetectGroup.GLOBAL)
            .setExample("Project1BDIO")
            .build();

    public static final NullablePathProperty DETECT_BINARY_SCAN_FILE =
        NullablePathProperty.newBuilder("detect.binary.scan.file.path")
            .setInfo("Binary Scan Target", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp(
                "If specified, this file and this file only will be uploaded for binary scan analysis. This property takes precedence over detect.binary.scan.file.name.patterns. The BINARY_SCAN tool does not provide project and version name defaults to Detect, so you need to set project and version names via properties when only the BINARY_SCAN tool is invoked.")
            .setGroups(DetectGroup.BINARY_SCANNER, DetectGroup.SOURCE_PATH)
            .build();

    public static final StringListProperty DETECT_BINARY_SCAN_FILE_NAME_PATTERNS =
        StringListProperty.newBuilder("detect.binary.scan.file.name.patterns", emptyList())
            .setInfo("Binary Scan Filename Patterns", DetectPropertyFromVersion.VERSION_6_0_0)
            .setHelp(
                "If specified, files in the source directory whose names match these file name patterns will be zipped and uploaded for binary scan analysis. This property will not be used if detect.binary.scan.file.path is specified. Search depth is controlled by property detect.binary.scan.search.depth. Directories specified via property detect.excluded.directories are excluded from the file search. This property accepts filename globbing-style wildcards. For more information, refer to the <xref href=\"https://documentation%2Eblackduck%2Ecom/bundle/detect/page/configuring/propertywildcards%2Ehtml\" scope=\"external\" outputclass=\"external\" format=\"html\" target=\"_blank\">Property wildcard support page.</xref>")
            .setGroups(DetectGroup.BINARY_SCANNER, DetectGroup.SOURCE_PATH)
            .setExample("*.jar")
            .build();

    // TODO: Consider raising default search depth
    public static final IntegerProperty DETECT_BINARY_SCAN_SEARCH_DEPTH =
        IntegerProperty.newBuilder("detect.binary.scan.search.depth", 0)
            .setInfo("Binary Scan Search Depth", DetectPropertyFromVersion.VERSION_6_9_0)
            .setHelp(
                "When binary scan filename patterns are being used to search for binary files to scan, this property sets the depth at which Detect will search for files (that match those patterns) to upload for binary scan analysis.")
            .setGroups(DetectGroup.BINARY_SCANNER, DetectGroup.SOURCE_SCAN)
            .build();

    public static final NullableStringProperty DETECT_CONTAINER_SCAN_FILE =
        NullableStringProperty.newBuilder("detect.container.scan.file.path")
            .setInfo("Container Scan Target", DetectPropertyFromVersion.VERSION_9_1_0)
            .setHelp(
                "If specified, this file and this file only will be uploaded for container scan analysis.",
                "Detect will accept either a user provided local file path, or remote HTTP/HTTPS URL to fetch a container image for scanning. The CONTAINER_SCAN tool does not provide project and version name defaults to Detect, so you need to set project and version names via properties when only the CONTAINER_SCAN tool is invoked."
            )
            .setGroups(DetectGroup.CONTAINER_SCANNER, DetectGroup.SOURCE_PATH)
            .build();

    // TODO: Consider removing environment sourcing code in 9.0.0. IDETECT-3167
    public static final StringProperty DETECT_BITBAKE_BUILD_ENV_NAME =
        StringProperty.newBuilder("detect.bitbake.build.env.name", "oe-init-build-env")
            .setInfo("BitBake Init Script Name", DetectPropertyFromVersion.VERSION_4_4_0)
            .setHelp("The name of the build environment init script.")
            .setGroups(DetectGroup.BITBAKE, DetectGroup.SOURCE_SCAN)
            .build();

    // TODO: Change to (Nullable?)StringProperty
    public static final StringListProperty DETECT_BITBAKE_PACKAGE_NAMES =
        StringListProperty.newBuilder("detect.bitbake.package.names", emptyList())
            .setInfo("BitBake Package Names", DetectPropertyFromVersion.VERSION_4_4_0)
            .setHelp("A comma-separated list of package names from which dependencies are extracted.")
            .setGroups(DetectGroup.BITBAKE, DetectGroup.SOURCE_SCAN)
            .build();

    // TODO: Consider removing environment sourcing code in 9.0.0. IDETECT-3167
    public static final StringListProperty DETECT_BITBAKE_SOURCE_ARGUMENTS =
        StringListProperty.newBuilder("detect.bitbake.source.arguments", emptyList())
            .setInfo("BitBake Source Arguments", DetectPropertyFromVersion.VERSION_6_0_0)
            .setHelp("A comma-separated list of arguments to supply when sourcing the build environment init script.")
            .setGroups(DetectGroup.BITBAKE, DetectGroup.SOURCE_SCAN)
            .build();

    // TODO: The task-depends.dot should be in working directory now, should verify? Maybe keep if IDETECT-3167 is completed
    public static final IntegerProperty DETECT_BITBAKE_SEARCH_DEPTH =
        IntegerProperty.newBuilder("detect.bitbake.search.depth", 1)
            .setInfo("BitBake Search Depth", DetectPropertyFromVersion.VERSION_6_1_0)
            .setHelp("The depth at which Detect will search for files generated by Bitbake.")
            .setGroups(DetectGroup.BITBAKE, DetectGroup.SOURCE_SCAN)
            .build();

    public static final NoneEnumListProperty<BitbakeDependencyType> DETECT_BITBAKE_DEPENDENCY_TYPES_EXCLUDED =
        NoneEnumListProperty.newBuilder("detect.bitbake.dependency.types.excluded", NoneEnum.NONE, BitbakeDependencyType.class)
            .setInfo("Bitbake Excluded Dependency Types", DetectPropertyFromVersion.VERSION_7_10_0)
            .setHelp(
                "The dependency types to exclude from the results.",
                "BUILD dependencies include recipes that are not declared in the license.manifest file, and native recipes. When excluding BUILD dependencies, Detect requires the license.manifest file (found under the {builddir}/tmp directory)."
            )
            .setExample("BUILD")
            .setGroups(DetectGroup.BITBAKE, DetectGroup.SOURCE_SCAN)
            .build();

    public static final NullableStringProperty DETECT_BLACKDUCK_SIGNATURE_SCANNER_ARGUMENTS =
        NullableStringProperty.newBuilder("detect.blackduck.signature.scanner.arguments")
            .setInfo("Signature Scanner Arguments", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp(
                "A space-separated list of additional arguments to use when running the Black Duck SCA signature scanner. Key-value pairs specified as arguments will replace the same entries specifed elswhere. Available signature scanner properties can be determined by specifying '--help' when executing the signature scanner jar file from the command line.",
                "Example usage: Running in bash on Linux and you want signature scanner to read a list of directories to exclude from the scan (using the signature scanner '--exclude-from' option). Configure signature scanner to read excluded directories from a file named excludes.txt in the current working directory with: --detect.blackduck.signature.scanner.arguments='--exclude-from ./excludes.txt'"
            )
            .setGroups(DetectGroup.SIGNATURE_SCANNER, DetectGroup.GLOBAL)
            .setExample("--exclude-from ./excludes.txt")
            .build();

    public static final NullablePathProperty DETECT_PROJECT_INSPECTOR_PATH =
        NullablePathProperty.newBuilder("detect.project.inspector.path")
            .setInfo("Project Inspector Path", DetectPropertyFromVersion.VERSION_8_1_0)
            .setHelp(
                "Use this property to point Detect to a local Project Inspector zip file, instead of the default Project Inspector zip file that Detect downloads from the binary repository. You need to ensure the version is compatible (the same major version that Detect downloads by default)."
            )
            .setGroups(DetectGroup.PROJECT_INSPECTOR, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    public static final NullableStringProperty PROJECT_INSPECTOR_GLOBAL_ARGUMENTS =
        NullableStringProperty.newBuilder("detect.project.inspector.global.arguments")
        .setInfo("Project Inspector Global Arguments", DetectPropertyFromVersion.VERSION_8_8_0)
        .setHelp(
            "A space-separated list of global options to pass to all invocations of the project inspector."
        )
        .setExample("--help --quiet")
        .setGroups(DetectGroup.PROJECT_INSPECTOR, DetectGroup.DEFAULT)
        .setCategory(DetectCategory.Advanced)
        .build();

    // TODO: JP don't like it
    public static final NullableStringProperty PROJECT_INSPECTOR_ARGUMENTS =
        NullableStringProperty.newBuilder("detect.project.inspector.arguments")
            .setInfo("Project Inspector Additional Arguments", DetectPropertyFromVersion.VERSION_7_7_0)
            .setHelp("A space-separated list of additional options to pass to all invocations of the project inspector.")
            .setGroups(DetectGroup.PROJECT_INSPECTOR, DetectGroup.DEFAULT)
            .setCategory(DetectCategory.Advanced)
            .build();

    // TODO: Consider detect.blackduck.signature.scanner.search=COPYRIGHT,LICENSE,ALL,NONE
    public static final BooleanProperty DETECT_BLACKDUCK_SIGNATURE_SCANNER_COPYRIGHT_SEARCH =
        BooleanProperty.newBuilder("detect.blackduck.signature.scanner.copyright.search", false)
            .setInfo("Signature Scanner Copyright Search", DetectPropertyFromVersion.VERSION_6_4_0)
            .setHelp("When set to true, user will be able to scan and discover copyright names in Black Duck SCA. Corresponding Signature Scanner CLI Argument: --copyright-search.")
            .setGroups(DetectGroup.SIGNATURE_SCANNER)
            .build();
    
    public static final BooleanProperty DETECT_BLACKDUCK_SIGNATURE_SCANNER_CSV_ARCHIVE =
            BooleanProperty.newBuilder("detect.blackduck.signature.scanner.csv.archive", false)
            .setInfo("Signature Scanner CSV Archive Output", DetectPropertyFromVersion.VERSION_10_2_0)
            .setHelp("When set to true Signature Scanner output will be in CSV format. Corresponding Signature Scanner CLI Argument: --outputFormat csv for offline mode, --upload-csv for online mode.")
            .setGroups(DetectGroup.SIGNATURE_SCANNER)
            .build();

    public static final BooleanProperty DETECT_BLACKDUCK_SIGNATURE_SCANNER_DRY_RUN =
        BooleanProperty.newBuilder("detect.blackduck.signature.scanner.dry.run", false)
            .setInfo("Signature Scanner Dry Run", DetectPropertyFromVersion.VERSION_4_2_0)
            .setHelp(
                "If set to true, the signature scanner results are not uploaded to Black Duck SCA, and the scanner results are written to disk via the Signature Scanner CLI argument: --dryRunWriteDir.")
            .setGroups(DetectGroup.SIGNATURE_SCANNER, DetectGroup.GLOBAL)
            .build();

    public static final ExtendedEnumProperty<ExtendedIndividualFileMatchingMode, IndividualFileMatching> DETECT_BLACKDUCK_SIGNATURE_SCANNER_INDIVIDUAL_FILE_MATCHING =
        ExtendedEnumProperty.newBuilderExtendedDefault(
                "detect.blackduck.signature.scanner.individual.file.matching",
                ExtendedIndividualFileMatchingMode.NONE,
                ExtendedIndividualFileMatchingMode.class,
                IndividualFileMatching.class
            )
            .setInfo("Individual File Matching", DetectPropertyFromVersion.VERSION_6_2_0)
            .setHelp("Users may set this property to indicate what types of files they want to match. Corresponding Signature Scanner CLI Argument: --individualFileMatching.")
            .setGroups(DetectGroup.SIGNATURE_SCANNER)
            .build();

    // TODO: Consider detect.blackduck.signature.scanner.search=COPYRIGHT,LICENSE,ALL,NONE
    public static final BooleanProperty DETECT_BLACKDUCK_SIGNATURE_SCANNER_LICENSE_SEARCH =
        BooleanProperty.newBuilder("detect.blackduck.signature.scanner.license.search", false)
            .setInfo("Signature Scanner License Search", DetectPropertyFromVersion.VERSION_6_2_0)
