/*
 * 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.processor.secure;

import com.scytl.slogger.CleanDirProvider;
import com.scytl.slogger.SecureAppender;
import com.scytl.slogger.SecureFileAppender;
import com.scytl.slogger.SecureFileAppenderTester;
import com.scytl.slogger.ValidatorSecureAppender;
import com.scytl.slogger.crypto.SecureLoggerKeyStore;
import com.scytl.slogger.validator.LogFilePatternReceiver;
import com.scytl.slogger.validator.ValidatorHelper;
import com.scytl.slogger.validator.hmac.encrypted.signed.PublicSignedEncryptedHMacEventValidator;
import com.scytl.slogger.validator.signature.SignatureEventValidator;

import org.apache.log4j.LogManager;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;

/**
 *
 */
public class SecureAppenderTest extends SecureFileAppenderTester {

  private static final int NUMBER_LOG_LINES = 100;

  protected static final String SIGNATURE_PKCS12_PASSWORD = "GXXCNJH48X5F649VRY52";

  protected static final String CIPHER_PKCS12_PASSWORD = "649VRY52GXXCNJH48X5F";

  protected static final String SIGNATURE_PKCS12 = "signatureKeyStore.p12";

  protected static final String CIPHER_PKCS12 = "cipherKeyStore.p12";

  @Override
  protected boolean containsMessage(String expectedMessage) throws IOException {
    File dir = CleanDirProvider.getTmpDir();
    File okFile = new File(dir, "ok.log");

    BufferedReader ok = new BufferedReader(new FileReader(okFile));
    String line;
    try {
      while ((line = ok.readLine()) != null) {
        if (line.contains(expectedMessage)) {
          return true;
        }
      }
    } finally {
      ok.close();
    }
    return false;
  }

  /**
   * @see com.scytl.slogger.SecureFileAppenderTester#createSecureAppender()
   */
  @Override
  protected SecureFileAppender createSecureAppender() throws Exception {
    SecureAppender sa = new ValidatorSecureAppender();
    sa.setPattern("%d %p - %C (%F:%L) [%t] - %m%n");
    sa.setFile(getTestLogFile().getAbsolutePath());

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

    sa.setSignaturePkcs12Certificate(getKeyStore(SIGNATURE_PKCS12, SIGNATURE_PKCS12_PASSWORD));
    sa.setCipherPkcs12Certificate(getKeyStore(CIPHER_PKCS12, CIPHER_PKCS12_PASSWORD));

    sa.setNumberLogLines(NUMBER_LOG_LINES);
    sa.setTimerLogMilliseconds(4000);
    sa.setTriggeringTime(1000);
    sa.setVerifyPattern("MESSAGE");
    sa.setVerifyPattern("TIMESTAMP LEVEL - CLASS (FILE:LINE) [THREAD] - MESSAGE");

    return sa;
  }

  protected PrivateKey getPrivateKey(final String keyStoreFile, final String password)
      throws GeneralSecurityException, IOException {

    SecureLoggerKeyStore keyStore = getCryptographyProvider().readKeyStore(
        Thread.currentThread().getContextClassLoader().getResourceAsStream(keyStoreFile),
        password.toCharArray());
    return getCryptographyProvider().getUserSignaturePrivateKey(keyStore, password.toCharArray());
  }

  protected PublicKey getPublicKey(final String keyStoreFile, final String password)
      throws GeneralSecurityException, IOException {
    SecureLoggerKeyStore keyStore = getCryptographyProvider().readKeyStore(
        Thread.currentThread().getContextClassLoader().getResourceAsStream(keyStoreFile),
        password.toCharArray());
    return getCryptographyProvider().getUserSignaturePublicKey(keyStore);
  }

  @Override
  protected void verifyLog() throws Exception {

    LogManager.shutdown();
    File dir = CleanDirProvider.getTmpDir();
    File okFile = new File(dir, "ok.log");
    File warnFile = new File(dir, "warn.log");
    File errorFile = new File(dir, "error.log");
    BufferedWriter ok = new BufferedWriter(new FileWriter(okFile));
    BufferedWriter warn = new BufferedWriter(new FileWriter(warnFile));
    BufferedWriter error = new BufferedWriter(new FileWriter(errorFile));

    try {

      PublicSignedEncryptedHMacEventValidator validator =
          new PublicSignedEncryptedHMacEventValidator(
              new SignatureEventValidator(getPublicKey(SIGNATURE_PKCS12, SIGNATURE_PKCS12_PASSWORD),
                  getCryptographyProvider()),
              ok, warn, error, getCryptographyProvider());
      ValidatorHelper.validateLog(_secureFileAppender, validator,
          LogFilePatternReceiver.DEFAULT_TIMESTAMP_FORMAT);
      assertNull(lastLogLine(errorFile, 0));
    } finally {
      ok.close();
      warn.close();
      error.close();
    }
  }

  private KeyStore getKeyStore(final String keyStoreFile, final String password)
      throws GeneralSecurityException, IOException {
    return getCryptographyProvider().readKeyStore(
        Thread.currentThread().getContextClassLoader().getResourceAsStream(keyStoreFile),
        password.toCharArray()).getKeyStore();
  }

  private String lastLogLine(final File log, final int expectedLines) throws Exception {
    BufferedReader br = new BufferedReader(new FileReader(log));
    try {
      String line = null;
      String lastLine = null;
      int lines = 0;
      while ((line = br.readLine()) != null) {
        lastLine = line;
        lines++;
      }

      if (expectedLines >= 0) {
        assertTrue(log.getAbsolutePath() + " expected log lines: " + expectedLines,
            lines == expectedLines);
      }
      return lastLine;
    } finally {
      br.close();
    }
  }

}
