package be.iminds.ilabt.jfed.lowlevel.api_wrapper;

import be.iminds.ilabt.jfed.CorePreferencesModule;
import be.iminds.ilabt.jfed.MockHttpRepliesTestCoreModule;
import be.iminds.ilabt.jfed.TestAuthoritiesModule;
import be.iminds.ilabt.jfed.TestUserModule;
import be.iminds.ilabt.jfed.log.Logger;
import be.iminds.ilabt.jfed.lowlevel.*;
import be.iminds.ilabt.jfed.lowlevel.api_wrapper.impl.ProtoGeniSAUserAndSliceApiWrapper;
import be.iminds.ilabt.jfed.lowlevel.connection.JFedConnectionProvider;
import be.iminds.ilabt.jfed.lowlevel.connection.MockReplyLibrary;
import be.iminds.ilabt.jfed.lowlevel.connection.MockSfaConnection;
import be.iminds.ilabt.jfed.preferences.JFedPreferences;
import be.iminds.ilabt.jfed.util.GeniUrn;
import com.google.inject.Guice;
import com.google.inject.Injector;
import org.apache.commons.lang.StringEscapeUtils;
import org.apache.xmlrpc.XmlRpcRequest;
import org.hamcrest.Matchers;
import org.mockito.Mockito;
import org.slf4j.LoggerFactory;
import org.testng.annotations.Test;

import java.security.PrivateKey;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Objects;

import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.*;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;

public class UserAndSliceApiWrapperTest {
    private static final org.slf4j.Logger LOG = LoggerFactory.getLogger(UserAndSliceApiWrapperTest.class);

    public static String wrapSuccessXmlRpcSAReply(String internal) {
        return "<?xml version=\"1.0\"?>\n" +
                "<methodResponse>\n" +
                "<params>\n" +
                "<param><value>" +
                "<struct>\n" +
                "<member><name>value</name><value>"+
                StringEscapeUtils.escapeXml(internal) +
                "</value>\n" +
                "</member>\n" +
                "<member><name>output</name><value><string></string></value></member>\n" +
                "<member><name>code</name><value><i4>0</i4></value></member>\n" +
                "</struct></value>\n" +
                "</param>\n" +
                "</params>\n" +
                "</methodResponse>\n";
    }

    @Test
    public void reconstructExpiresBug() throws JFedException {
        MockHttpRepliesTestCoreModule testCoreModule =  new MockHttpRepliesTestCoreModule();

        Injector injector = Guice.createInjector(
                testCoreModule,
                new TestUserModule(),
                new CorePreferencesModule(),
                new TestAuthoritiesModule()
        );
        final TestCertHelper testCertHelper = injector.getInstance(TestCertHelper.class);
        Logger logger = injector.getInstance(Logger.class);
        GeniUserProvider geniUserProvider = injector.getInstance(GeniUserProvider.class);
        JFedConnectionProvider connectionProvider = injector.getInstance(JFedConnectionProvider.class);
        JFedPreferences jFedPreferences = injector.getInstance(JFedPreferences.class);

        long nowMs = System.currentTimeMillis();
        //discard millisecond part
        nowMs -= nowMs % 1000l;
        final Date now = new Date(nowMs);
        final Date oneMin = new Date(nowMs + (60*1000l));
        final Date twoMin = new Date(nowMs + (2*60*1000l));
        final Date fourMin = new Date(nowMs + (4*60*1000l));

        testCoreModule.addMockReply(new MockSfaConnection.AbstractMockXmlRpcReply() {
            int callCount = 0;
            @Override
            public MockSfaConnection.MockXmlRpcAnswer matches(XmlRpcRequest request) {
                if (!Objects.equals(MockReplyLibrary.getServerUrl(request), TestAuthoritiesModule.urlA_pgSa1))
                    return null;
                if (Objects.equals(request.getMethodName(), "GetCredential") && request.getParameterCount() == 0) {
                    String targetUrn = TestCertHelper.userUrn;
                    X509Certificate ownerCert = testCertHelper.saCert;
                    X509Certificate targetCert = testCertHelper.userCert;
                    X509Certificate signerCert = testCertHelper.authority1Cert;
                    PrivateKey signerPrivateKey = testCertHelper.authority1Keys.getSshPrivateKey();
                    Date expireDate = callCount++ == 0 ? oneMin : fourMin;
                    String privilegeName = "*";
                    boolean canDelegate = true;
                    String credentialjFedName = "Test Credential for Test User";
                    SfaCredential cred = null;
                    try {
                        cred = SfaCredential.create(
                                "privilege",
                                TestCertHelper.saUrn/*ownerUrn*/, targetUrn,
                                ownerCert, targetCert,
                                signerCert, signerPrivateKey,
                                expireDate, privilegeName, canDelegate,
                                credentialjFedName);
                    } catch (CredentialException e) {
                        LOG.debug("Error creating test credential", e);
                        throw new RuntimeException(e);
                    }
                    String credentialXml = cred.getCredentialXml();

                    return new MockSfaConnection.MockXmlRpcAnswer(wrapSuccessXmlRpcSAReply(credentialXml));
                }
                return null;
            }
        });

//        UserAndSliceApiWrapper wrapper = new UniformFederationApi2UserAndSliceApiWrapper(logger, geniUserProvider, connectionProvider, jFedPreferences);
        UserAndSliceApiWrapper wrapper = new ProtoGeniSAUserAndSliceApiWrapper(logger, geniUserProvider, connectionProvider, jFedPreferences);

        List<AnyCredential> creds = wrapper.getUserCredentials(logger, GeniUrn.parse(TestCertHelper.userUrn));
        assertThat(creds, hasSize(1));
        assertThat(creds.get(0).getExpiresDate(), equalTo(oneMin));
        assertThat(wrapper.hasUserCredentials(), is(false));

        assertThat(creds, hasSize(1));
        creds = wrapper.getCachedUserCredentialsForAM();
        assertThat(creds, hasSize(0));
        assertThat(wrapper.hasUserCredentials(), is(false));

        creds = wrapper.getUserCredentials(logger, GeniUrn.parse(TestCertHelper.userUrn));
        assertThat(creds, hasSize(1));
        assertThat(creds.get(0).getExpiresDate(), equalTo(fourMin));
        assertThat(wrapper.hasUserCredentials(), is(true));
        creds = wrapper.getCachedUserCredentialsForAM();
        assertThat(creds, hasSize(1));
        assertThat(wrapper.hasUserCredentials(), is(true));

        assertThat("Not all calls had a mock reply",
                testCoreModule.getNoMockXmlRpcRequestInfo(),
                Matchers.empty());

    }

    @Test
    public void reconstructExpiresBug2a() throws JFedException {
        UserAndSliceApiWrapper testWrapper = mock(UserAndSliceApiWrapper.class, Mockito.CALLS_REAL_METHODS);

        long nowMs = System.currentTimeMillis();
        //discard millisecond part
        nowMs -= nowMs % 1000l;
        final Date now = new Date(nowMs);
        final Date oneMin = new Date(nowMs + (60*1000l));
        final Date twoMin = new Date(nowMs + (2*60*1000l));
        final Date fourMin = new Date(nowMs + (4*60*1000l));

        List<AnyCredential> credList = new ArrayList<AnyCredential>();
        SfaCredential cred = mock(SfaCredential.class);
        when(cred.getExpiresDate()).thenReturn(fourMin);
        credList.add(cred);

        assertThat(testWrapper.hasUserCredentials(credList), is(true));
    }

    @Test
    public void reconstructExpiresBug2b() throws JFedException {
        UserAndSliceApiWrapper testWrapper = mock(UserAndSliceApiWrapper.class, Mockito.CALLS_REAL_METHODS);

        long nowMs = System.currentTimeMillis();
        //discard millisecond part
        nowMs -= nowMs % 1000l;
        final Date now = new Date(nowMs);
        final Date oneMin = new Date(nowMs + (60*1000l));
        final Date twoMin = new Date(nowMs + (2*60*1000l));
        final Date fourMin = new Date(nowMs + (4*60*1000l));

        List<AnyCredential> credList = new ArrayList<AnyCredential>();
        SfaCredential cred = mock(SfaCredential.class);
        when(cred.getExpiresDate()).thenReturn(oneMin);
        credList.add(cred);

        assertThat(testWrapper.hasUserCredentials(credList), is(false));
    }
}