package com.airlock.iam.admin.application.service.license;

import com.airlock.iam.base.api.domain.model.validation.GenericError;
import com.airlock.iam.base.api.domain.model.validation.ValidationResult;
import com.airlock.iam.base.api.domain.model.validation.ValidationResultType;
import com.airlock.iam.base.api.domain.model.validation.Validator;
import com.airlock.iam.base.api.domain.model.validation.ValidatorError;
import com.airlock.iam.base.api.domain.model.validation.ValidatorError.ValidatorErrorBuilder;
import com.airlock.iam.base.misc.date.LocalDateHelper;
import com.airlock.iam.license.application.service.management.DetachedLicenseManager;
import com.airlock.iam.license.application.service.verification.store.LicenseUserCountingUserStore;
import com.airlock.iam.license.domain.model.verification.exception.LicenseDateViolation;
import com.airlock.iam.license.domain.model.verification.exception.LicenseInstanceCountViolation;
import com.airlock.iam.license.domain.model.verification.exception.LicenseMacViolation;
import com.airlock.iam.license.domain.model.verification.exception.LicenseDateViolation.LicenseDateViolationReason;
import com.airlock.iam.license.domain.model.verification.warnings.LicenseWarning;
import com.airlock.iam.license.domain.service.verification.exception.SystemLicenseViolation;
import java.util.List;
import java.util.stream.Collectors;

public class DetachedLicenseValidator implements Validator<String> {
   private static final String LICENSE_JSON_REQUEST_FIELD = "license";
   private static final String LICENSE_ARGS_TODAY = "today";
   private static final String LICENSE_ARGS_VALID_FROM = "validFrom";
   private static final String LICENSE_ARGS_VALID_UNTIL = "validUntil";
   private static final String LICENSE_ARGS_PRESENT_INSTANCES = "presentInstances";
   private static final String LICENSE_ARGS_LICENSED_INSTANCES = "licensedInstances";
   private static final String LICENSE_ARGS_PRESENT_MAC_ADDRESSES = "presentMacAddresses";
   private static final String LICENSE_ARGS_LICENSED_MAC_ADDRESSES = "licensedMacAddresses";
   private final DetachedLicenseManager detachedLicenseManager;

   public DetachedLicenseValidator(DetachedLicenseManager detachedLicenseManager) {
      this.detachedLicenseManager = detachedLicenseManager;
   }

   public ValidationResult validate(String licenseText) {
      List<ValidatorError> errors = this.detachedLicenseManager
         .checkIfSystemCoveredByLicense(licenseText)
         .stream()
         .map(DetachedLicenseValidator::mapViolation)
         .collect(Collectors.toList());
      return errors.isEmpty() ? ValidationResult.valid() : ValidationResult.invalid(errors);
   }

   public List<LicenseWarning> getLicenseWarnings(String licenseText, List<LicenseUserCountingUserStore> userCounters) {
      return this.detachedLicenseManager.collectLicenseWarnings(licenseText, userCounters);
   }

   private static ValidatorError mapViolation(SystemLicenseViolation violation) {
      ValidatorErrorBuilder errorBuilder = ValidatorError.builder();
      errorBuilder.field("license");
      if (violation instanceof LicenseInstanceCountViolation) {
         errorBuilder.type(DetachedLicenseValidator.LicenseViolation.INVALID_INSTANCES)
            .parameter("licensedInstances", ((LicenseInstanceCountViolation)violation).getNrOfLicensedInstances())
            .parameter("presentInstances", ((LicenseInstanceCountViolation)violation).getNrOfPresentInstances());
      } else if (violation instanceof LicenseDateViolation dateException) {
         LicenseDateViolationReason reason = dateException.getReason();
         if (reason == LicenseDateViolationReason.DATE_EXPIRED) {
            errorBuilder.type(DetachedLicenseValidator.LicenseViolation.EXPIRED)
               .parameter("today", LocalDateHelper.toLocalDate(dateException.getCurrentDate().getDate()))
               .parameter("validUntil", LocalDateHelper.toLocalDate(dateException.getValidUntil().getDate()));
         } else if (reason == LicenseDateViolationReason.DATE_EXPIRED_PERIOD) {
            errorBuilder.type(DetachedLicenseValidator.LicenseViolation.EXPIRED_PERIOD)
               .parameter("today", LocalDateHelper.toLocalDate(dateException.getCurrentDate().getDate()))
               .parameter("validFrom", LocalDateHelper.toLocalDate(dateException.getValidFrom().getDate()))
               .parameter("validUntil", LocalDateHelper.toLocalDate(dateException.getValidUntil().getDate()));
         } else if (reason == LicenseDateViolationReason.DATE_NOT_YET_VALID) {
            errorBuilder.type(DetachedLicenseValidator.LicenseViolation.NOT_YET_VALID)
               .parameter("today", LocalDateHelper.toLocalDate(dateException.getCurrentDate().getDate()))
               .parameter("validFrom", LocalDateHelper.toLocalDate(dateException.getValidFrom().getDate()));
         } else if (reason == LicenseDateViolationReason.DATE_NOT_YET_VALID_PERIOD) {
            errorBuilder.type(DetachedLicenseValidator.LicenseViolation.NOT_YET_VALID_PERIOD)
               .parameter("today", LocalDateHelper.toLocalDate(dateException.getCurrentDate().getDate()))
               .parameter("validFrom", LocalDateHelper.toLocalDate(dateException.getValidFrom().getDate()))
               .parameter("validUntil", LocalDateHelper.toLocalDate(dateException.getValidUntil().getDate()));
         }
      } else if (violation instanceof LicenseMacViolation) {
         errorBuilder.type(DetachedLicenseValidator.LicenseViolation.INVALID_MAC_ADDRESS)
            .parameter("licensedMacAddresses", ((LicenseMacViolation)violation).getLicensedMacAddrs().toString())
            .parameter("presentMacAddresses", ((LicenseMacViolation)violation).getSystemMacAddrs().toString());
      } else {
         errorBuilder.type(GenericError.WRONG_FORMAT);
      }

      return errorBuilder.build();
   }

   public enum LicenseViolation implements ValidationResultType {
      MISSING_LICENSE("MISSING_LICENSE"),
      INVALID_INSTANCES("INVALID_INSTANCES"),
      INVALID_MAC_ADDRESS("INVALID_MAC_ADDRESS"),
      EXPIRED("EXPIRED"),
      EXPIRED_PERIOD("EXPIRED_PERIOD"),
      NOT_YET_VALID("NOT_YET_VALID"),
      NOT_YET_VALID_PERIOD("NOT_YET_VALID_PERIOD");

      private final String id;

      LicenseViolation(String id) {
         this.id = id;
      }

      public String getId() {
         return this.id;
      }
   }
}
