package com.dbs.mplus.service;

import java.io.File;

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

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.dbs.mplus.MPlusConstants.HOSPITAL;
import com.dbs.mplus.framework.service.ConfigureService;
import com.dbs.mplus.framework.service.RememberMeService;
import com.dbs.mplus.framework.service.mobile.user.MpLoginInfoService;
import com.dbs.mplus.framework.util.HashUtil;
import com.dbs.mplus.framework.util.HttpRequestUtil;
import com.dbs.mplus.service.mobile.user.repository.dao.MPlusUser;

/**
 * 자동로그인 쿠키
 * 
 * @author jinsukjung
 * 
 */
@Service
public class RememberMeCookieBaker {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());

	@Autowired
	private AuthenticationDelegator authDelegator;
	@Autowired 
	private RememberMeService rememberMeService; 
	@Autowired 
	private ConfigureService configureService; 
	@Autowired
	private MpLoginInfoService mpLoginInfoService;
	
	private HashUtil hashUtil; 
	
	/**
	 * 자동로그인을 처리해주기 위해서 userName-구분자-password를 쿠키로 구워둔다. 
	 * @param request
	 * @param response
	 * @param userId
	 * @param plainPwd
	 * @param localCipherKey 
	 */
	public void create(HttpServletRequest request, HttpServletResponse response, String userId, String plainPwd, String hospitalCd, String deviceId, String localCipherKey) {
		String code = HOSPITAL.getCode(this.configureService.getServerServiceAddr(), request.getContextPath()); 
		
		// 정보 굽기 
		this.rememberMeService.create(request, response, code, userId, plainPwd, hospitalCd, deviceId, localCipherKey);
	}
	
	/**
	 * 로그아웃 시 자동로그인 쿠키를 제거합니다
	 * 
	 * @param request
	 * @return
	 */
	public void remove(HttpServletRequest request, HttpServletResponse response) {
		String code = HOSPITAL.getCode(this.configureService.getServerServiceAddr(), request.getContextPath()); 
		this.rememberMeService.remove(request, response, code);
	}
	
	/**
	 * 자동로그인 쿠키가 있으면 세션을 복원합니다
	 * 
	 * @param request
	 * @param response
	 * @param forcedPage 세션복원한 다음에 가야할 곳 
	 * 
	 * @return user : 복원된 사용자 정보 
	 * @throws Exception 복원해서 보니 비번재설정 필요 등 문제가 생긴 경우 
	 */
	public MPlusUser restore(HttpServletRequest request, HttpServletResponse response, String forcedPage) throws Exception {
		String code = HOSPITAL.getCode(this.configureService.getServerServiceAddr(), request.getContextPath()); 
		final String split[] = this.rememberMeService.restore(request, response, code); 
		
		if(split == null) { 
			return null; 
		}
		
		// 복원해서 로그인 처리 
		if (split.length > 1) {
			this.logger.debug("자동 로그인 처리 : " + split[0]);
			
			// 20180807 shinsunwoo 로그인 중복체크가 설정되는 경우, 자동로그인 기능과 상충되는 문제가 발생하여 아래의 로직을 추가함
			// 세션파일은 마지막 로그인의 위치정보를 기준으로 생성되었으며, 세션파일 정보가 일치할 때에 자동로그인을 지원하도록 수정함
			// 20190313 shinsunwoo 로그인 중복체크 로직 변경됨에 따라 복원로직이 변경됨(세션파일->DB세션정보로)
			String userId = split[0];
			String userDeviceId = split[3];
			if(userId != null) { 
				
				// 중복로그인 체크이면
				if( this.configureService.isSessionDuplicateCheck()){
					//// 세션파일 찾아서 
					//final File sessFile = this.getSessFile(request, userId);
					//
					//if(this.logger.isDebugEnabled()) { 
					//	this.logger.debug("세션파일 : 존재여부=" + sessFile.exists() + ", " + sessFile.getAbsolutePath());
					//}
					//
					//if( sessFile.exists()) {
					//	return this.authDelegator.authentication(request, response, split[0], split[1], split[2], split[3], forcedPage);	
					//}		
					
					// 현재 세션의 id와 uuid를 추출
					String currentUserId = userId; 
					String currentUserUuid = userDeviceId; 
					logger.debug("[restore:checkDuplicateLogin]currentUserId=" + currentUserId + ", currentUserUuid=" + currentUserUuid);
					
					// 1. DB에서 id에 해당하는 uuid 추출
					// 단독 db, 비부하 상태에서 등록 테스트에서 초당 200건 조회
					String dbUserUuid = mpLoginInfoService.getUuid(currentUserId);
					logger.debug("[restore:checkDuplicateLogin] DB에서 uuid 추출, dbUserUuid=" + dbUserUuid);
					
					// 2. 현 세션 uuid와 DB의 uuid를 비교하여 다른 경우, 현 세션을 제거하고 중복로그인을 알림
					if( !currentUserUuid.trim().equals( dbUserUuid.trim())){
						logger.debug("[restore:checkDuplicateLogin] currentUserUuid 와 dbUserUuid가 다름");
					} else {
						logger.debug("[restore:checkDuplicateLogin] currentUserUuid 와 dbUserUuid가 같음");
						return this.authDelegator.authentication(request, response, split[0], split[1], split[2], split[3], forcedPage);
					}
				} 
				else 
				{
					return this.authDelegator.authentication(request, response, split[0], split[1], split[2], split[3], forcedPage);
				}
			}
		}

		return null;
	}
	
	/**
	 * 사용자 ID에 해당하는 세션 파일을 반환함 
	 * @return
	 */
	private File getSessFile(HttpServletRequest request, String userId) {
		String deviceId = HttpRequestUtil.getUUID(request);
		
		// 없다면 IP로 대체하여 사용 
		if (StringUtils.isEmpty(deviceId)) {
			deviceId = HttpRequestUtil.getClientIpAddr(request);
		} 
		
		// hash 초기화 
		this.hashUtil = new HashUtil(this.configureService.getHashSalt()); 
		
		// 단방향 해쉬 처리해서 저장해두고 
		String hashClientId = this.hashUtil.sha256(deviceId);
		final String hashUserId = this.hashUtil.sha256(userId);
		final String filePath = this.configureService.getSessionRootDir() + "/" + this.getSubPath(hashUserId) + "/" + hashUserId;
		final String filePathAndName = filePath + "/" + hashClientId + ".sess";
		
		//실험용 
		//String filePathAndName = this.sessionDir + "/" + md5ClientId + ".sess"; 
		
		// 열어서
		final File file = new File(filePathAndName); 
		
		//logger.debug("[getSessionFile] session file : " + file.getAbsolutePath() + ", exists = " + file.exists());
		
		return file; 
	}
	
	/**
	 * sessionId를 기반으로 랜덤하게 디렉토리 만들기 
	 * @param sessionId
	 * @return
	 */
	private String getSubPath(String sessionId) {
		final StringBuffer sb = new StringBuffer();

		final int firstChar = sessionId.charAt(0);
		if ((firstChar & 1) == 0) {
			sb.append("e");
		} else {
			sb.append("o");
		}
		sb.append("/");

		final int secondChar = sessionId.charAt(5);
		final int modSecondChar = secondChar % 10;
		sb.append(String.valueOf((char) (97 + modSecondChar)));
		sb.append("/");

		final int thirdChar = sessionId.charAt(10);
		final int modthirdChar = thirdChar % 10;
		sb.append(String.valueOf((char) (107 + modthirdChar)));

		return sb.toString();
	}
}
