package org.xbib.elasticsearch.action.deploy;

import org.elasticsearch.ElasticSearchException;
import org.elasticsearch.action.support.nodes.TransportNodesOperationAction;
import org.elasticsearch.cluster.ClusterName;
import org.elasticsearch.cluster.ClusterService;
import org.elasticsearch.common.bytes.BytesReference;
import org.elasticsearch.common.inject.Inject;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.env.Environment;
import org.elasticsearch.threadpool.ThreadPool;
import org.elasticsearch.transport.TransportService;
import org.xbib.elasticsearch.plugin.gatherer.GathererPlugin;
import org.xbib.io.StreamUtil;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.atomic.AtomicReferenceArray;

public class TransportDeployAction extends TransportNodesOperationAction<DeployRequest, DeployResponse, DeployNodeRequest, DeployNodeResponse> {

    private final Environment environment;

    private final DeployService deployService;

    @Inject
    public TransportDeployAction(Settings settings, ClusterName clusterName, ThreadPool threadPool,
                                 ClusterService clusterService, TransportService transportService,
                                 Environment environment, DeployService deployService) {
        super(settings, clusterName, threadPool, clusterService, transportService);
        this.environment = environment;
        this.deployService = deployService;
    }

    @Override
    protected String transportAction() {
        return DeployAction.NAME;
    }

    @Override
    protected String executor() {
        return ThreadPool.Names.MANAGEMENT;
    }

    @Override
    protected DeployRequest newRequest() {
        return new DeployRequest();
    }

    @Override
    protected DeployResponse newResponse(DeployRequest request, AtomicReferenceArray nodesResponses) {
        return new DeployResponse();
    }

    @Override
    protected DeployNodeRequest newNodeRequest() {
        return new DeployNodeRequest();
    }

    @Override
    protected DeployNodeRequest newNodeRequest(String nodeId, DeployRequest request) {
        return new DeployNodeRequest(nodeId, request);
    }

    @Override
    protected DeployNodeResponse newNodeResponse() {
        return new DeployNodeResponse();
    }

    @Override
    protected DeployNodeResponse nodeOperation(DeployNodeRequest request) throws ElasticSearchException {
        String name = request.getRequest().getName();
        if (name == null) {
            throw new ElasticSearchException("no name given");
        }
        String path = request.getRequest().getPath();
        if (path == null) {
            throw new ElasticSearchException("no path given");
        }
        BytesReference ref = request.getRequest().getBytes();
        if (ref == null || ref.length() == 0) {
            throw new ElasticSearchException("no bytes in request");
        }
        // place all deployments under gatherer to avoid overwriting of other plugins
        File dir = new File(environment.pluginsFile(), GathererPlugin.NAME + "/" + name);
        if (dir.exists()) {
            throw new ElasticSearchException("refusing cowardly to overwrite existing path: " + dir.getAbsolutePath());
        }
        try {
            dir.mkdirs();
            File f = new File(path); // just to get file name
            File target = new File(dir, f.getName());
            logger.info("deploying to {}", target.getAbsolutePath());
            FileOutputStream out = new FileOutputStream(target);
            InputStream in = new ByteArrayInputStream(ref.array());
            StreamUtil.copy(in, out);
            in.close();
            out.close();
            // deploy service knows how to unpack archive and add jars to class path
            deployService.add(name, target.getAbsolutePath());
            // TODO set success result in DeployNodeResponse
        } catch (IOException e) {
            throw new ElasticSearchException(e.getMessage());
        }
        DeployNodeResponse response = new DeployNodeResponse();
        return response;
    }

    @Override
    protected boolean accumulateExceptions() {
        return true;
    }

}