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.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.Ordered;
import org.springframework.core.annotation.Order;
import org.springframework.security.oauth2.common.exceptions.InvalidRequestException;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

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;

@Configuration
@Order(Ordered.HIGHEST_PRECEDENCE) // 다른 HandlerExceptionResolver보다 우선 적용
public class CustomTokenEndpointExceptionResolver implements HandlerExceptionResolver {

	static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);

	@Autowired
	private TokenIssuanceLogDAO tokenIssuanceLogDAO;

	@Override
	public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,
			Exception ex) {

		// OAuth2 Token 요청 grant_type 누락 시
		if (ex instanceof InvalidRequestException) {
			response.setStatus(HttpServletResponse.SC_BAD_REQUEST);

			String errorMessage = MessageUtil.makeJsonErrorMessage(MessageUtil.ERROR_CODE_AUTH_FAIL, ex.getMessage());

			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, errorMessage, null);
				response.setContentType("application/json;charset=UTF-8");
				response.getWriter().write(errorMessage);
				response.getWriter().flush();
			} catch (IOException e) {
				e.printStackTrace();
			} finally {
				RequestContextData.ThreadLocalRequestContext.clear();
			}

			return new ModelAndView();
		}

		// 다른 예외는 다른 HandlerExceptionResolver에게 위임
		return null;
	}

	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);
		}
	}
}
