package cz.moneta.test.harness.connectors;

import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerEnvironmentList;
import itSodTesting.sharedResources.objects.dataTransfer.IlodsServerSourceSystem;
import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToClient;
import itSodTesting.sharedResources.objects.dataTransfer.ObjectTransferToServer;
import itSodTesting.sharedResources.utils.getUser.LoggedUser;
import org.apache.commons.lang3.tuple.Pair;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketTimeoutException;

public class IlodsServerConnector implements Connector {

    private static final Logger logger = LogManager.getLogger(IlodsServerConnector.class);

    private static final String token = "&PE$T.Q;1SXdhF@#$vdnv3,M8r5+dr=e";

    private static final Pair<String, String> liveServer = Pair.of("primary", "MBCZVW1DL0LRC02.mbid.cz");
    private static final Pair<String, String> backupServer = Pair.of("secondary", "MBCZVW0BL0LRC01.mbid.cz");
    private static final Pair<String, String> devServer = Pair.of("develop", "MBCZDWHQXX10366.mbid.cz");

    private Pair<String, String> currentServer = liveServer;
    // private Pair<String, String> currentServer = devServer;

    private IlodsServerEnvironmentList environment;

    public IlodsServerConnector(IlodsServerEnvironmentList environment) {
        this.environment = environment;
    }

    public ObjectTransferToClient sendRequest(ObjectTransferToServer data) {
        ObjectTransferToClient receivedObject = connectAndReceiveData(data);

        // no response = second try
        if (receivedObject == null) {
            switchServer();
            receivedObject = connectAndReceiveData(data);
        }

        if (receivedObject == null) {
            throw new RuntimeException("Cannot connect to Ilods servers");
        }

        return receivedObject;
    }

    private ObjectTransferToClient connectAndReceiveData(ObjectTransferToServer data) {
        ObjectTransferToClient receivedObject = null;
        Socket clientSocket = null;

        try {
            clientSocket = new Socket();
            clientSocket.connect(new InetSocketAddress(currentServer.getRight(), 51001), 1000);
            clientSocket.setSoTimeout(60 * 1000);

            ObjectOutputStream outToServer = new ObjectOutputStream(clientSocket.getOutputStream());
            ObjectInputStream inFromServer = new ObjectInputStream(clientSocket.getInputStream());

            // add clients informations do data
            data.setToken(token);
            data.setSourceSystem(IlodsServerSourceSystem.HARNESS);
            data.setMachineId(LoggedUser.getComputerName());
            data.setClientId(LoggedUser.getUserName());
            data.setEnvironment(environment);

            // pack and sending object data to server
            outToServer.writeObject(data);

            // unpack and reading object response from server
            Object transferedObject = inFromServer.readObject();
            receivedObject = (ObjectTransferToClient) transferedObject;

            // recognize and process errors
            if (receivedObject.getResultCode() == 12) {
                throw new RuntimeException("Fatal error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
            } else if (receivedObject.getResultCode() != 0) {
                throw new RuntimeException("Server error during processing " + data.getActionType() + ": " + receivedObject.getResultString());
            }

        } catch (SocketTimeoutException e) {
            logger.warn("Connection timed out - " + currentServer.getLeft() + " Ilods server didn't respond within the specified interval");
        } catch (ClassNotFoundException | IOException e) {
            logger.warn("Ilods server (" + currentServer.getLeft() + ") unavailable", e);
        } finally {
            closeConnection(clientSocket);
        }

        return receivedObject;
    }

    private void switchServer() {
        // Set up switch between primary/secondary Ilods Server (with check develop mode)
        if (currentServer == devServer) {
            return;
        }

        if (currentServer == liveServer) {
            currentServer = backupServer;
        } else {
            currentServer = liveServer;
        }
        logger.info("Trying to call " + currentServer.getLeft() + " Ilods server");
    }

    private void closeConnection(Socket socket) {
        if (socket == null)
            return;
        try {
            socket.shutdownInput();
        } catch (IOException e) {
        }
        try {
            socket.shutdownOutput();
        } catch (IOException e) {
        }
        try {
            socket.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
