/*
 * Copyright 2017 Crown Copyright
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package stroom.data.store.impl.fs;

import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import stroom.data.retention.impl.DataRetentionPolicyExecutor;
import stroom.data.retention.impl.DataRetentionRulesService;
import stroom.data.retention.shared.DataRetentionRule;
import stroom.data.retention.shared.DataRetentionRules;
import stroom.data.retention.shared.TimeUnit;
import stroom.data.shared.StreamTypeNames;
import stroom.data.store.api.Store;
import stroom.data.store.api.Target;
import stroom.data.store.api.TargetUtil;
import stroom.data.store.impl.fs.DataVolumeDao.DataVolume;
import stroom.docref.DocRef;
import stroom.index.impl.selection.VolumeConfig;
import stroom.meta.api.MetaProperties;
import stroom.meta.shared.Meta;
import stroom.meta.shared.MetaFields;
import stroom.node.api.NodeInfo;
import stroom.query.api.v2.ExpressionOperator;
import stroom.query.api.v2.ExpressionOperator.Op;
import stroom.query.api.v2.ExpressionTerm.Condition;
import stroom.test.AbstractCoreIntegrationTest;
import stroom.test.common.util.test.FileSystemTestUtil;
import stroom.util.time.StroomDuration;

import javax.inject.Inject;
import java.io.IOException;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.List;

import static org.assertj.core.api.Assertions.assertThat;

/**
 * Test the archiving stuff.
 * <p>
 * Create some old files and make sure they get archived.
 */
class TestDataRetentionPolicyExecutor extends AbstractCoreIntegrationTest {
    private static final int REPLICATION_COUNT = 1;
    private static final int SIXTY = 60;
    private static final int FIFTY_FIVE = 55;
    private static final int FIFTY = 50;

    @Inject
    private VolumeConfig volumeConfig;
    @Inject
    private Store streamStore;
    @Inject
    private DataVolumeService dataVolumeService;
    //    @Inject
//    private FeedStore feedStore;
    @Inject
    private FsCleanExecutor fileSystemCleanTaskExecutor;
    @Inject
    private NodeInfo nodeInfo;
    @Inject
    private DataRetentionRulesService dataRetentionRulesService;
    @Inject
    private DataRetentionPolicyExecutor dataRetentionPolicyExecutor;
    @Inject
    private PhysicalDeleteExecutor physicalDeleteExecutor;
    @Inject
    private DataStoreServiceConfig dataStoreServiceConfig;

    private int initialReplicationCount = 1;

    @BeforeEach
    void beforeEach() {
        initialReplicationCount = volumeConfig.getResilientReplicationCount();
        volumeConfig.setResilientReplicationCount(REPLICATION_COUNT);
    }

    @AfterEach
    void afterEach() {
        volumeConfig.setResilientReplicationCount(initialReplicationCount);
    }

    @Test
    void testCheckArchive() throws IOException {
        dataStoreServiceConfig.setFileSystemCleanOldAge(StroomDuration.ZERO);
        dataStoreServiceConfig.setDeletePurgeAge(StroomDuration.ZERO);
        fileSystemCleanTaskExecutor.clean();

        final ZonedDateTime oldDate = ZonedDateTime.now(ZoneOffset.UTC).minusDays(SIXTY);
        final ZonedDateTime newDate = ZonedDateTime.now(ZoneOffset.UTC).minusDays(FIFTY);

        // Write a file 2 files ... on we leave locked and the other not locked
        final String feedName = FileSystemTestUtil.getUniqueTestString();
//        final DocRef feedRef = feedStore.createDocument(feedName);

        setupDataRetentionRules(feedName);

        final MetaProperties oldFile = new MetaProperties.Builder()
                .feedName(feedName)
                .typeName(StreamTypeNames.RAW_EVENTS)
                .createMs(oldDate.toInstant().toEpochMilli())
                .build();
        final MetaProperties newFile = new MetaProperties.Builder()
                .feedName(feedName)
                .typeName(StreamTypeNames.RAW_EVENTS)
                .createMs(newDate.toInstant().toEpochMilli())
                .build();

        Meta oldFileMeta;
        try (final Target oldFileTarget = streamStore.openTarget(oldFile)) {
            oldFileMeta = oldFileTarget.getMeta();
            TargetUtil.write(oldFileTarget, "MyTest");
        }

        Meta newFileMeta;
        try (final Target newFileTarget = streamStore.openTarget(newFile)) {
            newFileMeta = newFileTarget.getMeta();
            TargetUtil.write(newFileTarget, "MyTest");
        }

        List<DataVolume> oldVolumeList = dataVolumeService
                .find(FindDataVolumeCriteria.create(oldFileMeta)).getValues();
        assertThat(oldVolumeList.size()).as("Expecting 1 stream volumes").isEqualTo(REPLICATION_COUNT);

        List<DataVolume> newVolumeList = dataVolumeService
                .find(FindDataVolumeCriteria.create(newFileMeta)).getValues();
        assertThat(newVolumeList.size()).as("Expecting 1 stream volumes").isEqualTo(REPLICATION_COUNT);

        dataRetentionPolicyExecutor.exec();
        physicalDeleteExecutor.exec();

        // Test Again
        oldVolumeList = dataVolumeService.find(FindDataVolumeCriteria.create(oldFileMeta)).getValues();
        assertThat(oldVolumeList.size()).as("Expecting 0 stream volumes").isEqualTo(0);

        newVolumeList = dataVolumeService.find(FindDataVolumeCriteria.create(newFileMeta)).getValues();
        assertThat(newVolumeList.size()).as("Expecting 1 stream volumes").isEqualTo(REPLICATION_COUNT);

        // Test they are
        oldVolumeList = dataVolumeService.find(FindDataVolumeCriteria.create(oldFileMeta)).getValues();
        assertThat(oldVolumeList.size()).as("Expecting 0 stream volumes").isEqualTo(0);
        newVolumeList = dataVolumeService.find(FindDataVolumeCriteria.create(newFileMeta)).getValues();
        assertThat(newVolumeList.size()).as("Expecting 1 stream volumes").isEqualTo(REPLICATION_COUNT);
    }

    private void setupDataRetentionRules(final String feedName) {
        final DocRef docRef = dataRetentionRulesService.createDocument("test");
        DataRetentionRules dataRetentionRules = dataRetentionRulesService.readDocument(docRef);

        final ExpressionOperator.Builder builder = new ExpressionOperator.Builder(true, Op.AND);
        builder.addTerm(MetaFields.FEED_NAME, Condition.EQUALS, feedName);
        final DataRetentionRule rule = createRule(1, builder.build(), FIFTY_FIVE, TimeUnit.DAYS);
        dataRetentionRules.setRules(Collections.singletonList(rule));
        dataRetentionRulesService.writeDocument(dataRetentionRules);
    }

    private DataRetentionRule createRule(final int num, final ExpressionOperator expression, final int age, final TimeUnit timeUnit) {
        return new DataRetentionRule(num, System.currentTimeMillis(), "rule " + num, true, expression, age, timeUnit, false);
    }
}
