package gov.va.med.edp.rpc;

import gov.va.med.exception.FoundationsException;
import gov.va.med.vistalink.adapter.cci.VistaLinkConnection;
import gov.va.med.vistalink.adapter.cci.VistaLinkAppProxyConnectionSpec;
import gov.va.med.vistalink.adapter.cci.VistaLinkDuzConnectionSpec;
import gov.va.med.vistalink.rpc.RpcRequest;
import gov.va.med.vistalink.rpc.RpcRequestFactory;
import gov.va.med.vistalink.rpc.RpcResponse;
import gov.va.med.vistalink.rpc.RpcRequestParams;
import junit.framework.TestCase;
import org.easymock.AbstractMatcher;
import org.easymock.ArgumentsMatcher;
import org.easymock.MockControl;
import org.easymock.internal.EqualsMatcher;

import javax.resource.cci.ConnectionFactory;
import javax.resource.ResourceException;
import java.util.List;

public abstract class AbstractVistaLinkConnectionTest extends TestCase {

    protected MockControl mockConnectionFactoryControl;
    protected ConnectionFactory mockConnectionFactory;
    protected MockControl mockVistaLinkConnectionControl;
    protected VistaLinkConnection mockVistaLinkConnection;
    protected MockConnectionFactoryLocator mockConnectionFactoryLocator;

    static ArgumentsMatcher RPC_REQUEST_MATCHER = new RpcRequestMatcher();

    protected void setUp() throws Exception {
        mockConnectionFactoryControl = MockControl.createControl(ConnectionFactory.class);
        mockConnectionFactory = (ConnectionFactory) mockConnectionFactoryControl.getMock();

        mockVistaLinkConnectionControl = MockControl.createControl(VistaLinkConnection.class);
        mockVistaLinkConnection = (VistaLinkConnection) mockVistaLinkConnectionControl.getMock();

        mockVistaLinkConnection.setTimeOut(VistaLinkRpcTemplate.DEFAULT_TIMEOUT);
        mockVistaLinkConnectionControl.setDefaultVoidCallable();

        mockConnectionFactoryLocator = new MockConnectionFactoryLocator();
        mockConnectionFactoryLocator.put(getStationNumber(), mockConnectionFactory);
    }

    protected abstract String getStationNumber();

    protected void expectVistaLinkDuzConnection(String duz) {
        try {
            mockConnectionFactoryControl.expectAndDefaultReturn(mockConnectionFactory.getConnection(new VistaLinkDuzConnectionSpec(getStationNumber(), duz)), mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }

    protected void expectVistaLinkAppProxyConnection(String appProxyName) {
        try {
            mockConnectionFactoryControl.expectAndDefaultReturn(mockConnectionFactory.getConnection(new VistaLinkAppProxyConnectionSpec(getStationNumber(), appProxyName)), mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }

    protected void expectRpcAndReturn(String rpcContext, String rpc, List params, RpcResponse response) {
        try {
            RpcRequest request = RpcRequestFactory.getRpcRequest(rpcContext, rpc);
            if (params != null) {
                request.setParams(params);
            }
            mockVistaLinkConnection.executeRPC(request);
            mockVistaLinkConnectionControl.setMatcher(RPC_REQUEST_MATCHER);
            mockVistaLinkConnectionControl.setReturnValue(response);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    protected void expectRpcAndReturn(String rpcContext, String rpc, String response) {
        expectRpcAndReturn(rpcContext, rpc, null, new TestRpcResponse(response));
    }

    protected void expectRpcAndReturn(String rpcContext, String rpc, List params, String response) {
        expectRpcAndReturn(rpcContext, rpc, params, new TestRpcResponse(response));
    }

    protected void expectRpcAndDefaultThrow(String rpcContext, String rpc, Throwable t) {
        try {
            mockVistaLinkConnection.executeRPC(RpcRequestFactory.getRpcRequest(rpcContext, rpc));
            mockVistaLinkConnectionControl.setMatcher(RPC_REQUEST_MATCHER);
            mockVistaLinkConnectionControl.setDefaultThrowable(t);
        } catch (FoundationsException e) {
            throw new IllegalArgumentException(e.toString());
        }
    }

    protected void replay() {
        mockConnectionFactoryControl.replay();
        mockVistaLinkConnectionControl.replay();
    }

    protected void verify() {
        mockConnectionFactoryControl.verify();
        mockVistaLinkConnectionControl.verify();
    }

    static String buildXmlResponse(String rpcResult) {
        StringBuffer buf = new StringBuffer();

        return buf.toString();
    }


    static class TestRpcResponse extends RpcResponse {
        protected TestRpcResponse(String rpcResult) {
            super(buildXmlResponse(rpcResult), buildXmlResponse(rpcResult), null, "gov.va.med.foundations.rpc.response", rpcResult, "flee");
        }
    }

    static class RpcRequestMatcher extends AbstractMatcher {

        protected boolean argumentMatches(Object o1, Object o2) {
            RpcRequest r1 = (RpcRequest) o1;
            RpcRequest r2 = (RpcRequest) o2;

            return r1.getRpcName().equals(r2.getRpcName()) &&
                    r1.getRpcContext().equals(r2.getRpcContext()) &&
                    r1.getRpcClientTimeOut() == r2.getRpcClientTimeOut() &&
                    r1.getTimeOut() == r2.getTimeOut() &&
                    parametersMatch(r1.getParams(), r2.getParams());
        }

        private boolean parametersMatch(RpcRequestParams p1, RpcRequestParams p2) {
            if (p1 == null && p2 == null) return true;
            if (p1 != null && p2 == null) return false;
            if (p1 == null && p2 != null) return false;
            for (int pos = 1; true; pos++) {
                Object v1 = p1.getParam(pos);
                Object v2 = p2.getParam(pos);
                if (v1 == null && v2 == null) return true;
                if (v1 != null && v2 == null) return false;
                if (v1 == null && v2 != null) return false;
                return MockControl.EQUALS_MATCHER.matches(new Object[] {v1}, new Object[] {v2});
            }
        }
    }
}
