/*
 * Copyright (c) MuleSoft, Inc.  All rights reserved.  http://www.mulesoft.com
 * The software in this package is published under the terms of the CPAL v1.0
 * license, a copy of which has been included with this distribution in the
 * LICENSE.txt file.
 */

package org.mule.runtime.module.artifact.internal.descriptor;

import static java.util.Objects.requireNonNull;
import static org.mule.runtime.api.i18n.I18nMessageFactory.createStaticMessage;
import static org.mule.runtime.core.api.config.MuleManifest.getProductVersion;
import org.mule.runtime.api.exception.MuleRuntimeException;
import org.mule.runtime.api.meta.MuleVersion;
import org.mule.runtime.module.artifact.api.descriptor.ArtifactDescriptor;
import org.mule.runtime.module.artifact.api.descriptor.ArtifactDescriptorValidator;

import java.util.function.Supplier;

/**
 * Validator for {@link ArtifactDescriptor#getMinMuleVersion()}. By default compares versions using {@code major.minor.patch} but
 * can be configured to do the comparison using semantic version.
 *
 * @since 4.1
 */
public class MinMuleVersionArtifactDescriptorValidator implements ArtifactDescriptorValidator {

  private boolean validateMinMuleVersionWithSemanticVersion;
  private Supplier<String> muleRuntimeVersionSupplier = () -> getProductVersion();

  /**
   * Creates an instance of this validator.
   *
   * @param validateMinMuleVersionWithSemanticVersion {@code true} uses semantic version for checking {@link ArtifactDescriptor#getMinMuleVersion()}.
   */
  public MinMuleVersionArtifactDescriptorValidator(boolean validateMinMuleVersionWithSemanticVersion) {
    this.validateMinMuleVersionWithSemanticVersion = validateMinMuleVersionWithSemanticVersion;
  }

  /**
   * Creates an instance of this validator.
   *
   * @param validateMinMuleVersionWithSemanticVersion {@code true} uses semantic version for checking {@link ArtifactDescriptor#getMinMuleVersion()}.
   * @param muleRuntimeVersionSupplier {@link Supplier} to get Mule Runtime version. Non null.
   */
  public MinMuleVersionArtifactDescriptorValidator(boolean validateMinMuleVersionWithSemanticVersion,
                                                   Supplier<String> muleRuntimeVersionSupplier) {
    this(validateMinMuleVersionWithSemanticVersion);
    requireNonNull(muleRuntimeVersionSupplier, "muleRuntimeVersionSupplier cannot be null");

    this.muleRuntimeVersionSupplier = muleRuntimeVersionSupplier;
  }

  @Override
  public void validate(ArtifactDescriptor descriptor) {
    MuleVersion minMuleVersion = descriptor.getMinMuleVersion();
    MuleVersion runtimeVersion = new MuleVersion(muleRuntimeVersionSupplier.get());
    runtimeVersion = new MuleVersion(runtimeVersion.toCompleteNumericVersion().replace("-" + runtimeVersion.getSuffix(), ""));
    if (validateMinMuleVersionWithSemanticVersion) {
      minMuleVersion = toBaseVersion(minMuleVersion);
      runtimeVersion = toBaseVersion(runtimeVersion);
    }

    doValidation(descriptor, minMuleVersion, runtimeVersion);
  }

  private MuleVersion toBaseVersion(MuleVersion version) {
    return new MuleVersion(version.getMajor() + "." + version.getMinor());
  }

  private void doValidation(ArtifactDescriptor descriptor, MuleVersion minMuleVersion, MuleVersion runtimeVersion) {
    if (runtimeVersion.priorTo(minMuleVersion)) {
      throw new MuleRuntimeException(
                                     createStaticMessage("Artifact %s requires a newest runtime version. Artifact required version is %s and Mule Runtime version is %s",
                                                         descriptor.getName(),
                                                         descriptor.getMinMuleVersion().toCompleteNumericVersion(),
                                                         runtimeVersion.toCompleteNumericVersion()));
    }
  }

}
