class BuildCacheUtils {

    @VisibleForTesting
    static void deleteOldCacheDirectories(
            @NonNull File sharedBuildCacheDir, @NonNull Duration cacheDirectoryLifeTime) {
        GradleVersion currentPluginVersion =
                Verify.verifyNotNull(
                        GradleVersion.tryParseAndroidGradlePluginVersion(
                                Version.ANDROID_GRADLE_PLUGIN_VERSION));
        for (File buildCacheDir : checkNotNull(sharedBuildCacheDir.listFiles())) {
            // First, make sure the path refers to a private cache directory
            if (!buildCacheDir.isDirectory()) {
                continue;
            }
            GradleVersion pluginVersion =
                    GradleVersion.tryParseAndroidGradlePluginVersion(buildCacheDir.getName());
            if (pluginVersion == null) {
                continue;
            }

            // Then, make sure we only delete private cache directories created by *previous*
            // plugin versions
            if (pluginVersion.compareTo(currentPluginVersion) >= 0) {
                continue;
            }

            // Find out the last time this cache was used
            File markerFile = new File(buildCacheDir, CACHE_USE_MARKER_FILE_NAME);
            long lastUsedTimestamp = markerFile.lastModified();
            if (lastUsedTimestamp == 0) {
                // The marker file does not yet exist, use the directory's timestamp instead
                lastUsedTimestamp = buildCacheDir.lastModified();
            }

            // Finally, delete the private cache directory if it has not been used in a while
            if (lastUsedTimestamp != 0
                    && Duration.ofMillis(System.currentTimeMillis() - lastUsedTimestamp)
                                    .compareTo(cacheDirectoryLifeTime)
                            >= 0) {
                // There could be a race condition here if another thread/process also reaches this
                // point, but that is Okay because the method call below is thread-safe and
                // process-safe, it's just that the code below will be executed more than once
                // (which is fine).
                try {
                    FileCache.getInstanceWithMultiProcessLocking(buildCacheDir).delete();
                    // Also delete the lock file. Note that it is generally not safe to delete lock
                    // files if some other thread/process might be using the cache, but since this
                    // cache (created and used only by an older plugin version) has not been used in
                    // a while, it is unlikely that it is suddenly being used now.
                    FileUtils.deleteIfExists(SynchronizedFile.getLockFile(buildCacheDir));
                } catch (IOException e) {
                    throw new UncheckedIOException(e);
                }
            }
        }
    }

}