// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.core.implementation.jackson;

import com.azure.core.implementation.SemanticVersion;
import com.azure.core.util.CoreUtils;
import com.azure.core.util.logging.ClientLogger;

/**
 * Provides information about Jackson package versions used, detects and logs errors.
 */
final class JacksonVersion {
    private SemanticVersion annotationsVersion;
    private SemanticVersion coreVersion;
    private SemanticVersion databindVersion;
    private SemanticVersion xmlVersion;
    private SemanticVersion jsr310Version;

    private static final String ANNOTATIONS_PACKAGE_NAME = "jackson-annotations";
    private static final String CORE_PACKAGE_NAME = "jackson-core";
    private static final String DATABIND_PACKAGE_NAME = "jackson-databind";
    private static final String XML_PACKAGE_NAME = "jackson-dataformat-xml";
    private static final String JSR310_PACKAGE_NAME = "jackson-datatype-jsr310";

    private static final SemanticVersion MIN_SUPPORTED_VERSION = SemanticVersion.parse("2.10.0");
    private static final SemanticVersion MAX_SUPPORTED_VERSION = SemanticVersion.parse("2.12.4");

    private static final String AZURE_CORE_PROPERTIES_NAME = "azure-core.properties";
    private static final String AZURE_CORE_PROPERTIES_VERSION_KEY = "version";

    private static final String AZURE_CORE_VERSION = CoreUtils
        .getProperties(AZURE_CORE_PROPERTIES_NAME)
        .getOrDefault(AZURE_CORE_PROPERTIES_VERSION_KEY, SemanticVersion.UNKNOWN_VERSION);

    private static JacksonVersion instance = null;

    private final String helpString;
    private final ClientLogger logger = new ClientLogger(JacksonVersion.class);

    private JacksonVersion() {
        annotationsVersion = SemanticVersion.getPackageVersionForClass("com.fasterxml.jackson.annotation.JsonProperty");
        coreVersion = SemanticVersion.getPackageVersionForClass("com.fasterxml.jackson.core.JsonGenerator");
        databindVersion = SemanticVersion.getPackageVersionForClass("com.fasterxml.jackson.databind.ObjectMapper");
        xmlVersion = SemanticVersion.getPackageVersionForClass("com.fasterxml.jackson.dataformat.xml.XmlMapper");
        jsr310Version = SemanticVersion.getPackageVersionForClass("com.fasterxml.jackson.datatype.jsr310.JavaTimeModule");
        checkVersion(annotationsVersion, ANNOTATIONS_PACKAGE_NAME);
        checkVersion(coreVersion, CORE_PACKAGE_NAME);
        checkVersion(databindVersion, DATABIND_PACKAGE_NAME);
        checkVersion(xmlVersion, XML_PACKAGE_NAME);
        checkVersion(jsr310Version, JSR310_PACKAGE_NAME);
        helpString = formatHelpString();
        logger.info(helpString);
    }

    /**
     * Returns help info containing actual detected package versions.
     *
     * @return diagnostics information with detected versions.
     */
    public String getHelpInfo() {
        return helpString;
    }

    /**
     * Gets {@code JacksonVersion} instance singleton.
     */
    public static synchronized JacksonVersion getInstance() {
        if (instance == null) {
            instance = new JacksonVersion();
        }

        return instance;
    }

    /**
     * Checks package version and logs if any issues detected.
     */
    private void checkVersion(SemanticVersion version, String packageName) {
        if (!version.isValid()) {
            logger.verbose("Could not find version of '{}'.", packageName);
            return;
        }

        if (version.compareTo(MIN_SUPPORTED_VERSION) < 0) {
            logger.error("Version '{}' of package '{}' is not supported (older than earliest supported version - `{}`), please upgrade.", version.getVersionString(), packageName, MIN_SUPPORTED_VERSION);
        }

        if (version.getMajorVersion() > MAX_SUPPORTED_VERSION.getMajorVersion()) {
            logger.error("Major version '{}' of package '{}' is newer than latest supported version - '{}'.",
                version.getVersionString(),
                packageName,
                MAX_SUPPORTED_VERSION.getVersionString());
        }
    }

    /**
     * Generates help information with versions detected in runtime.
     */
    private String formatHelpString() {
        // TODO(limolkova): add  link to troubleshooting docs
        return new StringBuilder()
            .append("Package versions: ")
            .append(ANNOTATIONS_PACKAGE_NAME)
            .append("=")
            .append(annotationsVersion.getVersionString())
            .append(", ")
            .append(CORE_PACKAGE_NAME)
            .append("=")
            .append(coreVersion.getVersionString())
            .append(", ")
            .append(DATABIND_PACKAGE_NAME)
            .append("=")
            .append(databindVersion.getVersionString())
            .append(", ")
            .append(XML_PACKAGE_NAME)
            .append("=")
            .append(xmlVersion.getVersionString())
            .append(", ")
            .append(JSR310_PACKAGE_NAME)
            .append("=")
            .append(jsr310Version.getVersionString())
            .append(", ")
            .append("azure-core=")
            .append(AZURE_CORE_VERSION)
            .toString();
    }
}

@@ -1004,16 +1004,129 @@
 jsr310%22;
+%0A    private static final String TROUBLESHOOTING_DOCS_LINK = %22https://aka.ms/azsdk/java/dependency/troubleshoot%22;
 %0A%0A    pr
@@ -1314,9 +1314,9 @@
 .12.
-4
+5
 %22);%0A
@@ -5310,16 +5310,148 @@
 ERSION)%0A
+            .append(%22, %22)%0A            .append(%22Troubleshooting version conflicts: %22)%0A            .append(TROUBLESHOOTING_DOCS_LINK)%0A
         

Add link to version conflict troubleshooting docs (#25177)