package com.agilex.healthcare.schedulingmanager.datalayer.vista.util;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.cxf.endpoint.Client;
import org.apache.cxf.frontend.ClientProxy;
import org.apache.cxf.transport.http.Cookie;
import org.apache.cxf.transport.http.HTTPConduit;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.oauth2.provider.DefaultAuthorizationRequest;
import org.springframework.security.oauth2.provider.OAuth2Authentication;

import com.agilex.healthcare.mobilehealthplatform.domain.MdwsKeys;
import com.agilex.healthcare.mobilehealthplatform.domain.MhpUser;
import com.agilex.healthcare.mobilehealthplatform.domain.PatientIdentifier;
import com.agilex.healthcare.mobilehealthplatform.mdws.MdwsInfo;
import com.agilex.healthcare.mobilehealthplatform.security.AppUser;
import com.agilex.healthcare.mobilehealthplatform.security.Roles;
import com.agilex.healthcare.schedulingmanager.mdws.MdwsResponseException;
import com.agilex.healthcare.schedulingmanager.mdws.MdwsResponseException.UserMessages;
import com.agilex.healthcare.schedulingmanager.mdws.connection.EmrConnection;
import com.agilex.healthcare.schedulingmanager.mdws.connection.MdwsConfiguration;
import com.agilex.healthcare.schedulingmanager.mdws.connection.VistaSite;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.emrservice.EmrSvcSoap;
import com.agilex.healthcare.schedulingmanager.mdws.generatedwsdl.emrservice.UserTO;

public class AuthenticationUtil {
	private static final org.apache.commons.logging.Log logger = org.apache.commons.logging.LogFactory.getLog(AuthenticationUtil.class);
	private static String LVENCOUNTER_SVC = "EmrSvc.asmx";
	private static String LVENCOUNTER_SVC_BASE_URL = "http://192.168.25.149/LVMdwsTest/";
	
	public static void loginUser(String siteCode, String accessCode, String verifyCode, String uniqueId) {
		loginUser(null, siteCode, accessCode, verifyCode, uniqueId);
	}

	public static void loginUser(MdwsConfiguration mdwsVistaConfiguration, String siteCode, String accessCode, String verifyCode, String uniqueId) {
		String service = null;
		String baseService = null;
		if (mdwsVistaConfiguration == null) {
			service = AuthenticationUtil.LVENCOUNTER_SVC;
			baseService = AuthenticationUtil.LVENCOUNTER_SVC_BASE_URL;
		}
		else {
			service = mdwsVistaConfiguration.getEndPointUrl();
			baseService = mdwsVistaConfiguration.getEndPointBaseUrl();
		}
		
		Authentication userAuthentication = createUserAuthentication(baseService, service, siteCode, accessCode, verifyCode, uniqueId);
		final Authentication oauthAuthentication = createOauth2Authentication(userAuthentication);
		
		SecurityContext context = new SecurityContext() {
			private static final long serialVersionUID = -7841498566548092084L;

			@Override
			public void setAuthentication(Authentication authentication) {
				
			}
			
			@Override
			public Authentication getAuthentication() {
				return oauthAuthentication;
			}
		};
		SecurityContextHolder.setContext(context);
	}
	
	public static void logoffUser() {
		try {
			MhpUser mhpUser = EmrConnection.getMhpUser();
			
			if (mhpUser == null)
				throw new MdwsResponseException(UserMessages.CONNECTION_ERROR, "mhpUser was null for logoffUser");
			if (mhpUser.getMdwsInfo() == null)
				throw new MdwsResponseException(UserMessages.CONNECTION_ERROR, "mhpUser.getMdwsInfo() was null for logoffUser");
			if (mhpUser.getMdwsInfo().getBaseUrl() == null)
				throw new MdwsResponseException(UserMessages.CONNECTION_ERROR, "mhpUser.getMdwsInfo().getBaseUrl() was null for logoffUser");
			
			String baseUrl = mhpUser.getMdwsInfo().getBaseUrl();
			String endPointUrl = AuthenticationUtil.LVENCOUNTER_SVC;
			MdwsConfiguration configuration = new MdwsConfiguration(baseUrl, endPointUrl, 0,0, new HashMap<String, VistaSite>());
			EmrConnection connectedPort = new EmrConnection(configuration, "ignored", true);
			connectedPort.getPort().disconnect();
			
			mhpUser.setMdwsInfo(null);
			SecurityContextHolder.clearContext();
		}
		catch (MdwsResponseException e) {
			logger.info("problem trying to close port connections: " + e.getMessage() + ": Expanded - " + e.getExpandedMessage());
		}
		catch (Exception e) {
			logger.info("problem trying to close port connections: " + e.getMessage());
		}
	}
	private static Authentication createUserAuthentication(String baseService, String service, String siteCode, String accessCode, String verifyCode, String uniqueId){
		List<GrantedAuthority> authorities = createPatientAuthorities();

		AppUser principal = createPrincipal(new PatientIdentifier(siteCode, uniqueId), authorities, baseService, service, siteCode, accessCode, verifyCode);
		
		Object credentials = null;

		Authentication authentication = createAuthentication(authorities, principal, credentials);
		return authentication;
	}
	
	
	public static EmrSvcSoap obtainUserConnection(MdwsConfiguration configuration, String siteCode, String accessCode, String verifyCode) {
		if (siteCode == null || siteCode.isEmpty())
			throw new MdwsResponseException(MdwsResponseException.UserMessages.INVALID_FACILITY_CODE, "The siteCode was null or empty");
		if (accessCode == null || accessCode.isEmpty())
			throw new MdwsResponseException(MdwsResponseException.UserMessages.INVALID_ACCESS_VERIFY, "The accessCode was null or empty");
		if (verifyCode == null || verifyCode.isEmpty())
			throw new MdwsResponseException(MdwsResponseException.UserMessages.INVALID_ACCESS_VERIFY, "The verifyCode was null or empty");
		
		EmrSvcSoap port = EmrConnection.establishConnection(configuration, siteCode, false);
	    
		UserTO result = port.login(accessCode, verifyCode, "");
		if (result.getFault() != null) {
			try {port.disconnect();} catch (Throwable t) { logger.info("Problem trying to dispose connection: " + t.getMessage());}
			throw new MdwsResponseException(MdwsResponseException.UserMessages.INVALID_ACCESS_VERIFY, result.getFault());
		}
		return port;
	}
	
	private static void loginLVEncounter(MhpUser mhpUser, String baseUrl, String endPointUrl, String siteCode, String accessCode, String verifyCode) {
		MdwsConfiguration configuration = new MdwsConfiguration(baseUrl, endPointUrl, 0,0, new HashMap<String, VistaSite>());
		
		EmrSvcSoap port = obtainUserConnection(configuration, siteCode, accessCode, verifyCode);

		String sessionId = getPortSessionId(port);
		updateMhpUser(mhpUser, sessionId, baseUrl, null);
	}
	public static void updateMhpUser(MhpUser mhpUser, String sessionId, String baseUrl, MdwsKeys keys) {
		String userId = mhpUser.getId();
		String username = mhpUser.getAccessCode();
		
		MdwsInfo mdwsInfo = new MdwsInfo();
		mdwsInfo.setUserId(userId);
		mdwsInfo.setUsername(username);
		mdwsInfo.setBaseUrl(baseUrl);
		mdwsInfo.setSessionId(sessionId);
		mdwsInfo.setKeys(keys);
		
		mhpUser.setMdwsInfo(mdwsInfo);
	}
	private static String getPortSessionId(EmrSvcSoap port) {
		Client client = ClientProxy.getClient(port);
		HTTPConduit httpConduit = (HTTPConduit)client.getConduit();
		
		Map<String, Cookie> cookies = httpConduit.getCookies();
		String sessionId = cookies.get("ASP.NET_SessionId").getValue();
		return sessionId;
	}
	
    protected static UsernamePasswordAuthenticationToken createAuthentication(List<GrantedAuthority> authorities, AppUser principal, Object credentials) {
        return new UsernamePasswordAuthenticationToken(principal, credentials, authorities);
    }
    protected static List<GrantedAuthority> createPatientAuthorities() {
		return createAuthorities(Roles.ROLE_CONSUMER);
	}
    protected static AppUser createPrincipal(PatientIdentifier patientIdentifier, List<GrantedAuthority> authorities, String baseService, String service, String siteCode, String accessCode, String verifyCode) {
		MhpUser authenticatedUser = new MhpUser();
		authenticatedUser.setAccessCode(accessCode);
        authenticatedUser.setUserIdentifier(patientIdentifier);
        authenticatedUser.setRightOfAccessAccepted(true);
        
        loginLVEncounter(authenticatedUser, baseService, service, siteCode, accessCode, verifyCode);
        
		AppUser principal = new AppUser(accessCode, "", authorities, authenticatedUser);
		return principal;
	}
	
	protected static List<GrantedAuthority> createAuthorities(String role) {
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		authorities.add(new SimpleGrantedAuthority(role));
		return authorities;
	}
    private static OAuth2Authentication createOauth2Authentication(Authentication userAuthentication){
    	return new OAuth2Authentication(new DefaultAuthorizationRequest(
    			Collections.<String, String> emptyMap()), userAuthentication);
    }
}
