package gov.va.med.edp.springframework.security.userdetails.vistalink;

import gov.va.med.edp.springframework.security.userdetails.VistaUserDetails;
import gov.va.med.edp.vistalink.AbstractVistaLinkConnectionTest;
import gov.va.med.vistalink.security.m.SecurityAccessVerifyCodePairInvalidException;
import gov.va.med.vistalink.adapter.record.VistaLinkFaultException;
import org.springframework.security.BadCredentialsException;
import org.springframework.dao.PermissionDeniedDataAccessException;
import org.easymock.MockControl;

import javax.resource.ResourceException;
import java.io.IOException;

public class VistaLinkUserDetailServiceTest extends AbstractVistaLinkConnectionTest {

    private static final String TEST_DUZ = "12345";
    private static final String TEST_STATION_NUMBER = "982";
    private static final String TEST_SIGNON_LOG_IEN = "3080311.14052001";

    private static final String TEST_ACCESS_CODE = "FOOBAR";
    private static final String TEST_VERIFY_CODE = "BARFOO";
    private static final String TEST_CLIENT_IP_ADDRESS = "10.0.1.201";
    private static final String TEST_APPLICATION_NAME = "Test Application Name";

    private VistaLinkUserDetailService userDetailService = new VistaLinkUserDetailService();

    protected String getStationNumber() {
        return TEST_STATION_NUMBER;
    }

    protected void setUp() throws Exception {
        super.setUp();
        setExpectedTimeOut(VistaLinkUserDetailService.DEFAULT_TIMEOUT);
        userDetailService.setConnectionFactoryLocator(mockConnectionFactoryLocator);
        userDetailService.setApplicationName(TEST_APPLICATION_NAME);
        userDetailService.afterPropertiesSet();
    }

    public void testDefaultTimeOut() {
        assertEquals(VistaLinkUserDetailService.DEFAULT_TIMEOUT, userDetailService.getRpcTemplate().getTimeOut());
    }

    public void testRequiredApplicationName() {
        try {
            userDetailService = new VistaLinkUserDetailService();
            userDetailService.setConnectionFactoryLocator(mockConnectionFactoryLocator);
            userDetailService.afterPropertiesSet();
            fail("expected illegal argument exception");
        } catch (IllegalArgumentException e) {

        }
    }

    public void testMissingCredentialsThrowsBadCredentials() {
        try {
            userDetailService.login(TEST_STATION_NUMBER, null, null, null);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
        try {
            userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, TEST_VERIFY_CODE, null);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
        try {
            userDetailService.login(TEST_STATION_NUMBER, null, TEST_VERIFY_CODE, TEST_CLIENT_IP_ADDRESS);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
        try {
            userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, null, TEST_CLIENT_IP_ADDRESS);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
    }

    //     public void testAuthenticateFailsForIncorrectPasswordCase() {
//        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("rod", "KOala");
//
//        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//        provider.setUserDetailsService(new MockAuthenticationDaoUserrod());
//        provider.setUserCache(new MockUserCache());
//
//        try {
//            provider.authenticate(token);
//            fail("Should have thrown BadCredentialsException");
//        } catch (BadCredentialsException expected) {
//            assertTrue(true);
//        }
//    }

    // test for expired credentials
    // test for bad credentials after an expired credentials result

    //    public void testAuthenticateFailsWithEmptyUsername() {
//        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, "koala");
//
//        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//        provider.setUserDetailsService(new MockAuthenticationDaoUserrod());
//        provider.setUserCache(new MockUserCache());
//
//        try {
//            provider.authenticate(token);
//            fail("Should have thrown BadCredentialsException");
//        } catch (BadCredentialsException expected) {
//            assertTrue(true);
//        }
//    }

    //

    public void testAuthenticateFailsWithInvalidPassword() throws IOException {
        expectVistaLinkAccessVerifyConnection(TEST_ACCESS_CODE, "flibberty-floo", TEST_CLIENT_IP_ADDRESS);
        expectRpcAndDefaultThrow(VistaLinkUserDetailService.RPC_CONTEXT, VistaLinkUserDetailService.GET_USER_INFO_RPC, createParams(TEST_CLIENT_IP_ADDRESS, TEST_APPLICATION_NAME), new PermissionDeniedDataAccessException("", new SecurityAccessVerifyCodePairInvalidException(new VistaLinkFaultException())));
        replay();

        try {
            userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, "flibberty-floo", TEST_CLIENT_IP_ADDRESS);
            fail("Should have thrown BadCredentialsException");
        } catch (BadCredentialsException expected) {
            assertTrue(true);
        }
    }

    public void testLogout() {
        MockControl userControl = MockControl.createControl(VistaUserDetails.class);
        VistaUserDetails user = (VistaUserDetails) userControl.getMock();

        userControl.expectAndReturn(user.getDuz(), TEST_DUZ);
        userControl.expectAndReturn(user.getLoginStationNumber(), TEST_STATION_NUMBER);
        userControl.expectAndReturn(user.getSignonLogInternalEntryNumber(), TEST_SIGNON_LOG_IEN);

        expectVistaLinkDuzConnection(TEST_DUZ);
        expectRpcAndReturn(VistaLinkUserDetailService.RPC_CONTEXT, VistaLinkUserDetailService.LOGOUT_RPC_NAME, createParams(TEST_SIGNON_LOG_IEN), "<foo/>");

        replay();
        userControl.replay();

        userDetailService.logout(user);

        verify();
        userControl.verify();
    }

    public void testLogin() throws IOException {
        expectVistaLinkAccessVerifyConnection(TEST_ACCESS_CODE, TEST_VERIFY_CODE, TEST_CLIENT_IP_ADDRESS);
        expectRpcAndReturnXmlResource(VistaLinkUserDetailService.RPC_CONTEXT, VistaLinkUserDetailService.GET_USER_INFO_RPC, createParams(TEST_CLIENT_IP_ADDRESS, TEST_APPLICATION_NAME), "successfulLoginResponse.xml");
        replay();

        VistaUserDetails user = userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, TEST_VERIFY_CODE, TEST_CLIENT_IP_ADDRESS);

        assertNotNull(user);
        assertEquals(TEST_STATION_NUMBER, user.getLoginStationNumber());
        assertEquals(TEST_DUZ, user.getDuz());
        assertEquals(TEST_SIGNON_LOG_IEN, user.getSignonLogInternalEntryNumber());
        assertEquals("Bar,Foo", user.getPersonName());
        assertEquals("Foo Bar", user.getDisplayName());
        assertEquals("BAR", user.getFamilyName());
        assertEquals("FOO", user.getGivenName());
        assertNull(user.getMiddleName());
        assertNull(user.getPrefix());
        assertNull(user.getSuffix());
        assertNull(user.getDegree());

        verify();
    }

    protected void expectVistaLinkAccessVerifyConnection(String accessCode, String verifyCode, String clientIpAddress) {
        try {
            mockConnectionFactoryControl.expectAndDefaultReturn(mockConnectionFactory.getConnection(new VistaLinkAccessVerifyConnectionSpec(getStationNumber(), accessCode, verifyCode, clientIpAddress)), mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }
}

/* source for java 5 and newer easymock
import gov.va.med.edp.springframework.security.userdetails.VistaUserDetails;
import static gov.va.med.edp.springframework.security.userdetails.vistalink.VistaLinkUserDetailService.*;
import gov.va.med.edp.vistalink.AbstractVistaLinkConnectionTest;
import gov.va.med.edp.springframework.security.userdetails.vistalink.VistaLinkAccessVerifyConnectionSpec;
import gov.va.med.edp.springframework.security.userdetails.vistalink.VistaLinkUserDetailService;
import gov.va.med.vistalink.security.m.SecurityAccessVerifyCodePairInvalidException;
import gov.va.med.vistalink.adapter.record.VistaLinkFaultException;
import org.springframework.security.BadCredentialsException;
import org.easymock.EasyMock;
import static org.easymock.EasyMock.expect;
import org.springframework.dao.PermissionDeniedDataAccessException;

import javax.resource.ResourceException;
import java.io.IOException;

public class VistaLinkUserDetailServiceTest extends AbstractVistaLinkConnectionTest {

    private static final String TEST_DUZ = "12345";
    private static final String TEST_STATION_NUMBER = "982";
    private static final String TEST_SIGNON_LOG_IEN = "3080311.14052001";

    private static final String TEST_ACCESS_CODE = "FOOBAR";
    private static final String TEST_VERIFY_CODE = "BARFOO";
    private static final String TEST_CLIENT_IP_ADDRESS = "10.0.1.201";
    private static final String TEST_APPLICATION_NAME = "Test Application Name";

    private VistaLinkUserDetailService userDetailService = new VistaLinkUserDetailService();

    protected String getStationNumber() {
        return TEST_STATION_NUMBER;
    }

    protected void setUp() throws Exception {
        super.setUp();
        setExpectedTimeOut(VistaLinkUserDetailService.DEFAULT_TIMEOUT);
        userDetailService.setConnectionFactoryLocator(mockConnectionFactoryLocator);
        userDetailService.setApplicationName(TEST_APPLICATION_NAME);
        userDetailService.afterPropertiesSet();
    }

    public void testDefaultTimeOut() {
        assertEquals(VistaLinkUserDetailService.DEFAULT_TIMEOUT, userDetailService.getRpcTemplate().getTimeOut());
    }

    public void testMissingCredentialsThrowsBadCredentials() {
        try {
            userDetailService.login(TEST_STATION_NUMBER, null, null, null);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
        try {
            userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, TEST_VERIFY_CODE, null);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
         try {
            userDetailService.login(TEST_STATION_NUMBER, null, TEST_VERIFY_CODE, TEST_CLIENT_IP_ADDRESS);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
         try {
            userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, null, TEST_CLIENT_IP_ADDRESS);
            fail("expected bad credentials exception");
        } catch (BadCredentialsException e) {
            // NOOP
        }
    }

    //     public void testAuthenticateFailsForIncorrectPasswordCase() {
//        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken("rod", "KOala");
//
//        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//        provider.setUserDetailsService(new MockAuthenticationDaoUserrod());
//        provider.setUserCache(new MockUserCache());
//
//        try {
//            provider.authenticate(token);
//            fail("Should have thrown BadCredentialsException");
//        } catch (BadCredentialsException expected) {
//            assertTrue(true);
//        }
//    }

    // test for expired credentials
    // test for bad credentials after an expired credentials result

    //    public void testAuthenticateFailsWithEmptyUsername() {
//        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, "koala");
//
//        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
//        provider.setUserDetailsService(new MockAuthenticationDaoUserrod());
//        provider.setUserCache(new MockUserCache());
//
//        try {
//            provider.authenticate(token);
//            fail("Should have thrown BadCredentialsException");
//        } catch (BadCredentialsException expected) {
//            assertTrue(true);
//        }
//    }
//
    public void testAuthenticateFailsWithInvalidPassword() throws IOException {
        expectVistaLinkAccessVerifyConnection(TEST_ACCESS_CODE, "flibberty-floo", TEST_CLIENT_IP_ADDRESS);
        expectRpcAndDefaultThrow(RPC_CONTEXT, GET_USER_INFO_RPC, createParams(TEST_CLIENT_IP_ADDRESS, TEST_APPLICATION_NAME), new PermissionDeniedDataAccessException("", new SecurityAccessVerifyCodePairInvalidException(new VistaLinkFaultException())));
        replay();

        try {
            userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, "flibberty-floo", TEST_CLIENT_IP_ADDRESS);
            fail("Should have thrown BadCredentialsException");
        } catch (BadCredentialsException expected) {
           assertTrue(true);
        }
    }

    public void testLogout() {
        VistaUserDetails user = EasyMock.createMock(VistaUserDetails.class);
        expect(user.getDuz()).andReturn(TEST_DUZ);
        expect(user.getLoginStationNumber()).andReturn(TEST_STATION_NUMBER);
        expect(user.getSignonLogInternalEntryNumber()).andReturn(TEST_SIGNON_LOG_IEN);

        expectVistaLinkDuzConnection(TEST_DUZ);
        expectRpcAndReturn(RPC_CONTEXT, LOGOUT_RPC_NAME, createParams(TEST_SIGNON_LOG_IEN), "<foo/>");

        replay();
        EasyMock.replay(user);

        userDetailService.logout(user);

        verify();
        EasyMock.verify(user);
    }

    public void testLogin() throws IOException {
        expectVistaLinkAccessVerifyConnection(TEST_ACCESS_CODE, TEST_VERIFY_CODE, TEST_CLIENT_IP_ADDRESS);
        expectRpcAndReturnXmlResource(RPC_CONTEXT, GET_USER_INFO_RPC, createParams(TEST_CLIENT_IP_ADDRESS, TEST_APPLICATION_NAME), "successfulLoginResponse.xml");
        replay();

        VistaUserDetails user = userDetailService.login(TEST_STATION_NUMBER, TEST_ACCESS_CODE, TEST_VERIFY_CODE, TEST_CLIENT_IP_ADDRESS);

        assertNotNull(user);
        assertEquals(TEST_STATION_NUMBER, user.getLoginStationNumber());
        assertEquals(TEST_DUZ, user.getDuz());
        assertEquals(TEST_SIGNON_LOG_IEN, user.getSignonLogInternalEntryNumber());
        assertEquals("Bar,Foo", user.getPersonName());
        assertEquals("Foo Bar", user.getDisplayName());
        assertEquals("BAR", user.getFamilyName());
        assertEquals("FOO", user.getGivenName());
        assertNull(user.getMiddleName());
        assertNull(user.getPrefix());
        assertNull(user.getSuffix());
        assertNull(user.getDegree());

        verify();
    }

    protected void expectVistaLinkAccessVerifyConnection(String accessCode, String verifyCode, String clientIpAddress) {
        try {
            org.easymock.EasyMock.expect(mockConnectionFactory.getConnection(new VistaLinkAccessVerifyConnectionSpec(getStationNumber(), accessCode, verifyCode, clientIpAddress))).andReturn(mockVistaLinkConnection);
        } catch (ResourceException e) {
            fail("unexpected exception: " + e.getMessage());
        }
    }
}
*/