/*-
 * ~~~~~~licensing~~~~~~
 * artifact-management
 * ==========
 * Copyright (C) 2020 - 2025 EmDev LLC
 * ==========
 * You may not use this file except in accordance with the License Terms of the Copyright
 * Holder located at: https://entaxy.ru/eula . All copyrights, all intellectual property
 * rights to the Software and any copies are the property of the Copyright Holder. Unless
 * it is explicitly allowed the Copyright Holder, the User is prohibited from using the
 * Software for commercial purposes to provide services to third parties.
 * 
 * The Copyright Holder hereby declares that the Software is provided on an "AS IS".
 * Under no circumstances does the Copyright Holder guarantee or promise that the
 * Software provided by him will be suitable or not suitable for the specific purposes
 * of the User, that the Software will meet all commercial and personal subjective
 * expectations of the User, that the Software will work properly, without technical
 * errors, quickly and uninterruptedly.
 * 
 * Under no circumstances shall the Copyright Holder or its Affiliates is not liable
 * to the User for any direct or indirect losses of the User, his expenses or actual
 * damage, including, downtime; loss of bussines; lost profit; lost earnings; loss
 * or damage to data, property, etc.
 * ~~~~~~/licensing~~~~~~
 */
package ru.entaxy.platform.core.artifact.repository.impl.remote;

import javax.net.ssl.SSLContext;

import org.apache.http.HttpRequest;
import org.apache.http.auth.AuthenticationException;
import org.apache.http.auth.UsernamePasswordCredentials;
import org.apache.http.client.HttpResponseException;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.config.Registry;
import org.apache.http.config.RegistryBuilder;
import org.apache.http.conn.socket.ConnectionSocketFactory;
import org.apache.http.conn.socket.PlainConnectionSocketFactory;
import org.apache.http.conn.ssl.NoopHostnameVerifier;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.auth.BasicScheme;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.BasicHttpClientConnectionManager;
import org.apache.http.ssl.SSLContexts;
import org.apache.http.ssl.TrustStrategy;

import ru.entaxy.platform.base.support.CommonUtils;
import ru.entaxy.platform.core.artifact.Artifact;

public class NexusRepository extends AbstractRemoteRepository {

    private static final String DEFAULT_REPOSITORY_NAME = "entaxy";
    private static final String NEXUS_ADDRESS = "/service/rest/v1";

    String username;
    String password;
    String host;
    String repositoryName;

    String hostSuffix;

    String apiAddress;

    boolean isSystem;

    @Override
    public void init(RemoteRepositoryDescriptor repositoryDescriptor) {
        this.host = repositoryDescriptor.getHost();

        String repositoryPath = repositoryDescriptor.getRepositoryPath();
        if (CommonUtils.isValid(repositoryPath))
            repositoryPath = repositoryPath.trim();
        if (!CommonUtils.isValid(repositoryPath))
            // TODO throw exception
            return;
        if (repositoryPath.endsWith("/"))
            repositoryPath = repositoryPath.substring(0, repositoryPath.length() - 1);
        String[] splitted = repositoryPath.split("/");
        this.repositoryName = splitted[splitted.length - 1];
        if (!CommonUtils.isValid(repositoryName))
            this.repositoryName = DEFAULT_REPOSITORY_NAME;

        this.hostSuffix = repositoryDescriptor.getHostSuffix();

        this.apiAddress = this.host + this.hostSuffix + NEXUS_ADDRESS;

        this.username = repositoryDescriptor.getUsername();
        this.password = repositoryDescriptor.getPassword();

        this.isSystem = repositoryDescriptor.isSystem();
    }

    @Override
    public void deploy(Artifact artifact) throws Exception {

        // TODO: manage via config
        TrustStrategy acceptingTrustStrategy = (cert, authType) -> true;
        SSLContext sslContext = SSLContexts.custom().loadTrustMaterial(null, acceptingTrustStrategy).build();
        SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext,
                NoopHostnameVerifier.INSTANCE);

        Registry<ConnectionSocketFactory> socketFactoryRegistry =
                RegistryBuilder.<ConnectionSocketFactory>create()
                        .register("https", sslsf)
                        .register("http", new PlainConnectionSocketFactory())
                        .build();

        try (BasicHttpClientConnectionManager connectionManager =
                new BasicHttpClientConnectionManager(socketFactoryRegistry)) {

            HttpPost httpPost = new HttpPost(this.apiAddress + "/components?repository=" + this.repositoryName);

            addAuth(httpPost);
            httpPost.setEntity(createMultipartEntityBuilder(artifact).build());

            try (CloseableHttpClient httpClient = HttpClients.custom().setSSLSocketFactory(sslsf)
                    .setConnectionManager(connectionManager).build();
                 CloseableHttpResponse response = httpClient.execute(httpPost)) {
                checkAnswer(response);
            }
        }

    }

    private void checkAnswer(CloseableHttpResponse response) throws HttpResponseException {
        if (response.getStatusLine().getStatusCode() != 204 && response.getStatusLine().getStatusCode() != 200) {
            throw new HttpResponseException(response.getStatusLine().getStatusCode(),
                    response.getStatusLine().getReasonPhrase());
        }
    }

    private void addAuth(HttpRequest httpRequest) throws AuthenticationException {
        if (CommonUtils.isValid(username)) {
            UsernamePasswordCredentials creds = new UsernamePasswordCredentials(username, password);
            httpRequest.addHeader(new BasicScheme().authenticate(creds, httpRequest, null));
        }
    }

    /*
    private List getListArtifactInfo(CloseableHttpResponse response) throws IOException {
        if (response.getEntity() != null && response.getEntity().getContent() != null) {
            Map content = new Gson().fromJson(IOUtils.toString(response.getEntity().getContent()), Map.class);
            return (List) content.get("items");
        }
        return null;
    }
    */

    private MultipartEntityBuilder createMultipartEntityBuilder(Artifact artifact) {
        MultipartEntityBuilder builder = MultipartEntityBuilder.create();
        builder.addBinaryBody("maven2.asset1", artifact.asByteArray());
        builder.addTextBody("maven2.groupId", artifact.getCoordinates().getGroupId());
        builder.addTextBody("maven2.artifactId", artifact.getCoordinates().getArtifactId());
        builder.addTextBody("maven2.version", artifact.getCoordinates().getVersion());
        builder.addTextBody("maven2.asset1.extension", artifact.getCoordinates().getType());
        builder.addTextBody("maven2.asset1.classifier", artifact.getCoordinates().getClassifier());

        return builder;
    }


}
