package org.jboss.installer.validators;

import org.apache.commons.lang3.StringUtils;
import org.eclipse.aether.DefaultRepositorySystemSession;
import org.eclipse.aether.RepositorySystem;
import org.jboss.galleon.ProvisioningException;
import org.jboss.installer.core.InstallerRuntimeException;
import org.jboss.installer.core.LanguageUtils;
import org.jboss.installer.core.ValidationResult;
import org.wildfly.channel.ArtifactTransferException;
import org.wildfly.channel.Channel;
import org.wildfly.channel.ChannelManifestCoordinate;
import org.wildfly.channel.ChannelSession;
import org.wildfly.channel.InvalidChannelMetadataException;
import org.wildfly.channel.NoStreamFoundException;
import org.wildfly.channel.UnresolvedRequiredManifestException;
import org.wildfly.channel.VersionResult;
import org.wildfly.channel.maven.VersionResolverFactory;
import org.wildfly.prospero.api.MavenOptions;
import org.wildfly.prospero.wfchannel.MavenSessionManager;

import java.io.IOException;
import java.net.URL;
import java.util.List;

import static org.jboss.installer.postinstall.task.jsf.MyFacesJsfLibrarySetup.MYFACES_API_ARTIFACT_ID;
import static org.jboss.installer.postinstall.task.jsf.MyFacesJsfLibrarySetup.MYFACES_GROUP_ID;
import static org.jboss.installer.postinstall.task.jsf.MyFacesJsfLibrarySetup.MYFACES_IMPL_ARTIFACT_ID;
import static org.jboss.installer.validators.JsfValidator.JSF_VERSION_NOT_SUPPORTED_KEY;

public class RepositoryContentValidator {

    public static final String ARTIFACT_NOT_FOUND = "jsf_install.artifact_not_in_repository";
    public static final String INVALID_MANIFEST = "jsf_install.invalid_manifest";
    private final LanguageUtils langUtils;

    public RepositoryContentValidator(LanguageUtils langUtils) {
        this.langUtils = langUtils;
    }

    public ValidationResult validate(URL repository, String groupId, String artifactId, String version) {
        String path = repository.getPath();
        if (!path.endsWith("/")) {
            path += "/";
        }
        path += groupId.replace('.', '/');
        path += "/" + artifactId;
        path += "/" + version;
        path += "/" + artifactId + "-" + version + ".jar";
        final String query = repository.getQuery();
        try {
            URL url = new URL(repository.getProtocol(), repository.getHost(), repository.getPort(), path + (query==null?"":query));
            url.openConnection().getInputStream().close();
        } catch (IOException e) {
            return ValidationResult.error(langUtils.getString(ARTIFACT_NOT_FOUND, groupId, artifactId, version, repository.toExternalForm()));
        }

        return ValidationResult.ok();
    }

    public ValidationResult validateManifest(URL repositories, String groupId, String artifactId) {
        return validateManifest(List.of(repositories), groupId, artifactId);
    }

    public ValidationResult validateManifest(List<URL> repositories, String groupId, String artifactId) {
        final MavenSessionManager msm;
        try {
            msm = new MavenSessionManager(MavenOptions.builder().setNoLocalCache(true).build());
        } catch (ProvisioningException e) {
            throw new InstallerRuntimeException("Unable to create temporary maven repository", e);
        }
        final RepositorySystem mvnSystem = msm.newRepositorySystem();
        final DefaultRepositorySystemSession mvnSession = msm.newRepositorySystemSession(mvnSystem);

        final Channel.Builder channel = new Channel.Builder()
                .setManifestCoordinate(new ChannelManifestCoordinate(groupId, artifactId))
                ;
        for (int i = 0; i < repositories.size(); i++) {
                channel.addRepository("myfaces-"+i, repositories.get(i).toExternalForm());
        }

        String repositoriesText = StringUtils.join(repositories, ",");

        final ChannelSession session;
        try {
            session = new ChannelSession(List.of(channel.build()), new VersionResolverFactory(mvnSystem, mvnSession));
        } catch (ArtifactTransferException | UnresolvedRequiredManifestException e) {
            return ValidationResult.error(langUtils.getString(ARTIFACT_NOT_FOUND, groupId, artifactId, "", repositoriesText));
        } catch (InvalidChannelMetadataException e) {
            return ValidationResult.error(langUtils.getString(INVALID_MANIFEST, groupId + ":" + artifactId, String.join("\n", e.getValidationMessages())));
        }

        final VersionResult apiVersion;
        try {
            apiVersion = session.findLatestMavenArtifactVersion(MYFACES_GROUP_ID, MYFACES_API_ARTIFACT_ID, "jar", null, null);
        } catch (ArtifactTransferException | NoStreamFoundException e) {
            return ValidationResult.error(langUtils.getString(ARTIFACT_NOT_FOUND, MYFACES_GROUP_ID, MYFACES_API_ARTIFACT_ID, "", repositoriesText));
        }
        final VersionResult implVersion;
        try {
            implVersion = session.findLatestMavenArtifactVersion(MYFACES_GROUP_ID, MYFACES_IMPL_ARTIFACT_ID, "jar", null, null);
        } catch (ArtifactTransferException | NoStreamFoundException e) {
            return ValidationResult.error(langUtils.getString(ARTIFACT_NOT_FOUND, MYFACES_GROUP_ID, MYFACES_IMPL_ARTIFACT_ID, "", repositoriesText));
        }
        final ValidationResult versionRes = CombiningValidator.of(()->validateVersion(apiVersion), ()->validateVersion(implVersion)).validate();
        if (versionRes.getResult() != ValidationResult.Result.OK) {
            return versionRes;
        }

        try {
            session.resolveMavenArtifact(MYFACES_GROUP_ID, MYFACES_API_ARTIFACT_ID, "jar", null, null);
        } catch (ArtifactTransferException | NoStreamFoundException e) {
            return ValidationResult.error(langUtils.getString(ARTIFACT_NOT_FOUND, MYFACES_GROUP_ID, MYFACES_API_ARTIFACT_ID, apiVersion.getVersion(), repositoriesText));
        }
        try {
            session.resolveMavenArtifact(MYFACES_GROUP_ID, MYFACES_IMPL_ARTIFACT_ID, "jar", null, null);
        } catch (ArtifactTransferException | NoStreamFoundException e) {
            return ValidationResult.error(langUtils.getString(ARTIFACT_NOT_FOUND, MYFACES_GROUP_ID, MYFACES_IMPL_ARTIFACT_ID, implVersion.getVersion(), repositoriesText));
        }

        return ValidationResult.ok();
    }

    private ValidationResult validateVersion(VersionResult apiVersion) {
        final JsfUtils jsfUtils = new JsfUtils();
        final String cleanedVer = jsfUtils.cleanedVer(apiVersion.getVersion());
        if (cleanedVer == null || cleanedVer.isEmpty()) {
            return ValidationResult.error(langUtils.getString(JSF_VERSION_NOT_SUPPORTED_KEY, apiVersion.getVersion()));
        }
        return ValidationResult.ok();
    }
}
