package com.eactive.eai.authserver.config;

import java.io.IOException;
import java.time.LocalDateTime;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint;

import com.eactive.eai.authserver.dao.TokenIssuanceLogDAO;
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.authserver.vo.ClientVO;
import com.eactive.eai.common.logger.EAIDBLogControl;
import com.eactive.eai.common.util.Logger;
import com.eactive.eai.common.util.MessageUtil;
import com.eactive.eai.data.entity.onl.authserver.TokenIssuanceLog;
import com.fasterxml.jackson.databind.ObjectMapper;

public class CustomOAuth2AuthenticationEntryPoint extends OAuth2AuthenticationEntryPoint {
	
	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

    private final ObjectMapper objectMapper = new ObjectMapper();
    
    private final TokenIssuanceLogDAO tokenIssuanceLogDAO;
    
    public CustomOAuth2AuthenticationEntryPoint(TokenIssuanceLogDAO dao) {
        this.tokenIssuanceLogDAO = dao;
    }

    @Override
    public void commence(HttpServletRequest request,
                         HttpServletResponse response,
                         AuthenticationException authException) throws IOException {
    	
    	try {
            RequestContextData data = new RequestContextData();
            data.setClientId(request.getParameter("client_id"));
            data.setIpAddress(extractIpAddress(request));
            data.setGrantType(request.getParameter("grant_type"));
            data.setScope(request.getParameter("scope"));
            data.setUsername(request.getParameter("username"));
            data.setResource(request.getParameter("resource"));

            RequestContextData.ThreadLocalRequestContext.set(data);
            logTokenIssuance(false,false, authException.getMessage(), null);
        } finally {
            RequestContextData.ThreadLocalRequestContext.clear();
        }

        response.setContentType("application/json;charset=UTF-8");
        response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
        
        String errorMessage = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, authException.getMessage());
        
        response.getWriter().write(errorMessage);
        response.getWriter().flush();
    }
    
    private String extractIpAddress(HttpServletRequest request) {
        String ipAddress = request.getHeader("X-Forwarded-For");
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_CLIENT_IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        if (ipAddress == null || ipAddress.length() == 0 || "unknown".equalsIgnoreCase(ipAddress)) {
            ipAddress = request.getRemoteAddr();
        }
        return ipAddress;
    }
    
    private void logTokenIssuance(boolean isSuccess, boolean isRestrictionExempt, String resultMessage, String accessToken) {
		try {

			RequestContextData data = RequestContextData.ThreadLocalRequestContext.get();
			OAuth2Manager.getInstance();

			TokenIssuanceLog log = new TokenIssuanceLog();
			String clientId = data.getClientId();
			log.setClientId(clientId);

			if(StringUtils.isNotBlank(clientId)) {
				ClientVO clientVO = OAuth2Manager.getInstance().getClientInfo(clientId);
				log.setAppName(clientVO.getClientName());
				log.setOrgId(clientVO.getOrgId());
				log.setOrgName(clientVO.getOrgName());
			}

			log.setGrantType(data.getGrantType());
			log.setScope(data.getScope());
			log.setIpAddress(data.getIpAddress());
			log.setSuccessYn(isSuccess ? "Y" : "N");
			log.setIssuanceDateTime(LocalDateTime.now());
			log.setRestrictionExemptYn(isRestrictionExempt ? "Y" : "N");
			log.setResultMessage(resultMessage);
			log.setAccessToken(accessToken); // Add access token value to the log
			
			if( !isSuccess ) {
				logger.error("Token request[/oauth/token or /oauth2/token] => clientId: {}, grantType: {}, scope: {}",
						clientId, data.getGrantType(), data.getScope());
			}
			
			if (!EAIDBLogControl.isEnable()) {
				logger.warn("DB logging is disabled. Skipping token issuance logging.");
				return;
			}

			tokenIssuanceLogDAO.saveTokenIssuanceLog(log);
		} catch (Throwable th) {
			logger.error("Error while logging token issuance: " + th.getMessage(), th);
		}
	}
}


