/*
 * Copyright 2018 Scytl Secure Electronic Voting SA
 *
 * All rights reserved
 *
 * See our extended copyright notice in *file 'Copyright.txt'
 * which is part of this source code package
 */

package com.scytl.slogger;

import static java.nio.file.Files.lines;
import static org.junit.Assert.assertTrue;

import com.scytl.slogger.policy.LogFileNamePolicies;
import com.scytl.slogger.policy.LogFileNamePolicyException;

import org.apache.log4j.Level;
import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import java.io.File;
import java.io.IOException;
import java.util.Iterator;
import java.util.stream.Stream;

/**
 * Test for verifying the secure checkpoint generation as last log file lines
 */
public class RollingFileCheckpointTest extends AbstractSecureAppenderTest {

  private static final int MAX_TIME_FOR_EACH_LOG_FILE_IN_MILLIS = 100;

  private static final String CHECKPOINT_MESSAGE = "New Secret Key generated";

  private static final int MAX_LOG_MESSAGES_BETWEEN_CHECKPOINTS = 5;

  @Override
  @Before
  public void setUp() throws Exception {
    super.setUp();
    initLogger();
  }

  @Test
  public void testEachFileIsFinishedByCheckpoint() throws LogFileNamePolicyException, IOException {
    Logger logger = Logger.getRootLogger();
    logger.addAppender(secureAppender);

    logger.setLevel(Level.INFO);

    for (int i = 0; i < 4; i++) {
      logger.info("1st Log input #" + i);
    }

    sleep(MAX_TIME_FOR_EACH_LOG_FILE_IN_MILLIS);

    for (int i = 0; i < 3; i++) {
      logger.info("2nd Log input #" + i);
    }

    secureAppender.close();
    logger.removeAppender(secureAppender);

    for (Iterator<File> it = secureAppender.getLogFileNamePolicy().logFiles(); it.hasNext();) {
      File logFile = it.next();
      String line = getLastLineOf(logFile);

      assertTrue("checkpoint message not found in file:" + logFile + "\nline:" + line,
          line.contains(CHECKPOINT_MESSAGE));
    }
  }

  @Test
  public void testNewFileIsCreatedWhenTriggeringTimePassed()
      throws LogFileNamePolicyException, IOException {
    Logger logger = Logger.getRootLogger();
    logger.addAppender(secureAppender);

    logger.setLevel(Level.INFO);

    for (int i = 0; i < 23; i++) {
      logger.info("1st Log input #" + i);
    }

    sleep(MAX_TIME_FOR_EACH_LOG_FILE_IN_MILLIS);

    for (int i = 0; i < 17; i++) {
      logger.info("2nd Log input #" + i);
    }

    sleep(MAX_TIME_FOR_EACH_LOG_FILE_IN_MILLIS);

    for (int i = 0; i < 12; i++) {
      logger.info("3rd Log input #" + i);
    }

    logger.removeAppender(secureAppender);
    secureAppender.close();

    int fileCounter = 0;
    for (Iterator<File> it = secureAppender.getLogFileNamePolicy().logFiles(); it
        .hasNext(); fileCounter++) {
      it.next();
    }
    assertTrue("The number of files is smaller than expected: " + fileCounter, fileCounter >= 3);
  }

  private String getLastLineOf(File logFile) throws IOException {
    try (Stream<String> lines = lines(logFile.toPath())) {
      return lines.reduce(null, (r, l) -> l);
    }
  }

  private void initLogger() throws Exception {

    secureAppender = new SecureAppender();
    secureAppender.setName(NAME);
    secureAppender.setPattern(PATTERN);
    secureAppender.setVerifyPattern(VERIFY_PATTERN);
    secureAppender.setFile(testLogFile.getAbsolutePath());

    secureAppender.setSignaturePkcs12FileName(SIGNATURE_PKCS12_FILE);
    secureAppender.setCipherPkcs12FileName(CIPHER_PKCS12_FILE);

    secureAppender.setSignaturePkcs12Password(SIGNATURE_PKCS12_PASSWORD);
    secureAppender.setCipherPkcs12Password(CIPHER_PKCS12_PASSWORD);

    secureAppender.setNumberLogLines(MAX_LOG_MESSAGES_BETWEEN_CHECKPOINTS);
    secureAppender.setTimerLogMilliseconds(0);
    secureAppender.setTriggeringTime(MAX_TIME_FOR_EACH_LOG_FILE_IN_MILLIS);
    secureAppender.setLogFileNamePolicyName(LogFileNamePolicies.ROLLING_SEQUENTIAL_FILENAME.name());
    secureAppender.activateOptions();
  }

  private void sleep(final int millis) {
    try {
      Thread.sleep(millis);
    } catch (InterruptedException e) {
      e.printStackTrace();
    }
  }

}
