public static Optional<IndexSetValidator.Violation> validate(ElasticsearchConfiguration elasticsearchConfiguration,
                                                                 IndexLifetimeConfig retentionConfig) {
        Period indexLifetimeMin = retentionConfig.indexLifetimeMin();
        Period indexLifetimeMax = retentionConfig.indexLifetimeMax();
        final Period leeway = indexLifetimeMax.minus(indexLifetimeMin);
        if (leeway.toStandardSeconds().getSeconds() < 0) {
            return Optional.of(IndexSetValidator.Violation.create(f("%s <%s> is shorter than %s <%s>", FIELD_INDEX_LIFETIME_MAX, indexLifetimeMax,
                    FIELD_INDEX_LIFETIME_MIN, indexLifetimeMin)));
        }

        if (leeway.toStandardSeconds().isLessThan(elasticsearchConfiguration.getTimeSizeOptimizingRotationPeriod().toStandardSeconds())) {
            return Optional.of(IndexSetValidator.Violation.create(f("The duration between %s and %s <%s> cannot be shorter than %s <%s>", FIELD_INDEX_LIFETIME_MAX, FIELD_INDEX_LIFETIME_MIN,
                    leeway, TIME_SIZE_OPTIMIZING_ROTATION_PERIOD, elasticsearchConfiguration.getTimeSizeOptimizingRotationPeriod())));
        }

        Period fixedLeeway = elasticsearchConfiguration.getTimeSizeOptimizingRetentionFixedLeeway();
        if (Objects.nonNull(fixedLeeway) && leeway.toStandardSeconds().isLessThan(fixedLeeway.toStandardSeconds())) {
            return Optional.of(IndexSetValidator.Violation.create(f("The duration between %s and %s <%s> cannot be shorter than %s <%s>", FIELD_INDEX_LIFETIME_MAX, FIELD_INDEX_LIFETIME_MIN,
                    leeway, TIME_SIZE_OPTIMIZING_RETENTION_FIXED_LEEWAY, fixedLeeway)));
        }


        final Period maxRetentionPeriod = elasticsearchConfiguration.getMaxIndexRetentionPeriod();
        if (maxRetentionPeriod != null
                && indexLifetimeMax.toStandardSeconds().isGreaterThan(maxRetentionPeriod.toStandardSeconds())) {
            return Optional.of(IndexSetValidator.Violation.create(f("Lifetime setting %s <%s> exceeds the configured maximum of %s=%s.",
                    FIELD_INDEX_LIFETIME_MAX, indexLifetimeMax,
                    ElasticsearchConfiguration.MAX_INDEX_RETENTION_PERIOD, maxRetentionPeriod)));
        }

        if (periodOtherThanDays(indexLifetimeMax) && !elasticsearchConfiguration.allowFlexibleRetentionPeriod()) {
            return Optional.of(IndexSetValidator.Violation.create(f("Lifetime setting %s <%s> can only be a multiple of days",
                    FIELD_INDEX_LIFETIME_MAX, indexLifetimeMax)));
        }
        if (periodOtherThanDays(indexLifetimeMin) && !elasticsearchConfiguration.allowFlexibleRetentionPeriod()) {
            return Optional.of(IndexSetValidator.Violation.create(f("Lifetime setting %s <%s> can only be a multiple of days",
                    FIELD_INDEX_LIFETIME_MIN, indexLifetimeMin)));
        }
        return Optional.empty();
    }
@Test
    public void timeBasedSizeOptimizingHonorsFixedLeeWay() {
        when(elasticConfig.getTimeSizeOptimizingRotationPeriod()).thenReturn(Period.days(1));
        when(elasticConfig.getTimeSizeOptimizingRetentionFixedLeeway()).thenReturn(Period.days(10));

        IndexLifetimeConfig config = IndexLifetimeConfig.builder()
                .indexLifetimeMin(Period.days(10))
                .indexLifetimeMax(Period.days(19))
                .build();

        assertThat(validate(elasticConfig, config)).hasValueSatisfying(v -> assertThat(v.message())
                .contains("The duration between index_lifetime_max and index_lifetime_min <P9D> " +
                        "cannot be shorter than time_size_optimizing_retention_fixed_leeway <P10D>"));

        assertThat(validate(elasticConfig, config.toBuilder().indexLifetimeMax(Period.days(20)).build())).isEmpty();
    }