/*
 * 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.validator.hmac.encrypted.signed;

import com.scytl.slogger.crypto.SecureLoggerCryptographyProvider;
import com.scytl.slogger.event.SecureLoggingEvent;
import com.scytl.slogger.event.SecureMessageProperties;
import com.scytl.slogger.event.SecurePatternLayout;
import com.scytl.slogger.validator.EventValidatorException;
import com.scytl.slogger.validator.hmac.encrypted.PublicEncryptedHMacEventValidator;
import com.scytl.slogger.validator.signature.SignatureEventValidator;

import java.io.BufferedWriter;
import java.io.IOException;
import java.util.Base64;
import java.util.List;
import java.util.Map;

/**
 * Public signed encrypted HMAC event validator.
 */
public class PublicSignedEncryptedHMacEventValidator extends PublicEncryptedHMacEventValidator {

  private final SignatureEventValidator signatureEventValidator;

  private SecureLoggingEvent currentSignatureEvent;

  private SecureLoggingEvent lastEvent;

  public PublicSignedEncryptedHMacEventValidator(
      final SignatureEventValidator signatureEventValidator, final BufferedWriter okWriter,
      final BufferedWriter warnWriter, final BufferedWriter errorWriter,
      final SecureLoggerCryptographyProvider cryptographyProvider) {
    super(okWriter, warnWriter, errorWriter, cryptographyProvider);
    this.signatureEventValidator = signatureEventValidator;
  }

  @Override
  public void stopValidation(final SecurePatternLayout layout) throws EventValidatorException {
    List<SecureLoggingEvent> events = currentBlock.logEvents();
    if (events.size() > 1 || (lastEvent != null && !lastEvent.getSecureMessage().properties()
        .containsKey(SecureMessageProperties.SIGNATURE))) {
      for (SecureLoggingEvent event : events) {
        EventValidatorException exception =
            new EventValidatorException("HMac Session Key not found", event, layout);
        currentBlock.addProcessedLogEvent(event, exception);
      }
      try {
        super.saveCurrentBlock(layout);
      } catch (IOException e) {
        throw new EventValidatorException(ERROR_WHILE_WRITING_THE_FILE, e);
      }
    }
  }

  @Override
  public void validateEvent(final SecureLoggingEvent event, final SecurePatternLayout layout)
      throws EventValidatorException {
    Map<String, String> eventProperties = event.getSecureMessage().properties();
    if (eventProperties.containsKey(SecureMessageProperties.ENCRYPTED_SESSION_KEY)
        && eventProperties.containsKey(SecureMessageProperties.SIGNATURE)) {
      currentSignatureEvent = event;
    }

    lastEvent = event;
    super.validateEvent(event, layout);
  }

  @Override
  protected void saveCurrentBlock(final SecurePatternLayout layout) throws IOException {

    if (currentSignatureEvent == null) {
      currentBlock.addProcessedLogEvent(lastEvent,
          new EventValidatorException("Missing secure block signature", lastEvent, layout));
    } else {
      String event = layout.format(currentSignatureEvent);
      if (!event.contains(Base64.getEncoder().encodeToString(getLastHmac()))) {
        currentBlock.addProcessedLogEvent(currentSignatureEvent, new EventValidatorException(
            "Signature doesn't contain last HMac", currentSignatureEvent, layout));
      } else {
        try {
          signatureEventValidator.validateEvent(currentSignatureEvent, layout);
          currentBlock.addProcessedLogEvent(currentSignatureEvent);
        } catch (EventValidatorException e) {
          EventValidatorException exception =
              new EventValidatorException("Invalid signature", currentSignatureEvent, layout);
          exception.initCause(e);
          currentBlock.addProcessedLogEvent(currentSignatureEvent, exception);
        }
      }
    }
    currentSignatureEvent = null;
    super.saveCurrentBlock(layout);
  }
}
