package com.dbs.mplus.service;

import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

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;
import com.dbs.mplus.exception.mobile.ApiCallException;
import com.dbs.mplus.framework.FrameworkConstants;
import com.dbs.mplus.framework.exception.MPlusServiceException;
import com.dbs.mplus.framework.exception.service.LoginException;
import com.dbs.mplus.framework.util.HashUtil;
import com.dbs.mplus.service.api.MPlusApi;
import com.dbs.mplus.service.api.MPlusApiCallService;
import com.dbs.mplus.service.mobile.user.MPUserService;
import com.dbs.mplus.service.mobile.user.repository.dao.MPlusUser;

import net.sf.json.JSONArray;

/**
 * 사용자를 인증하고, 어느 페이지로 넘어가야할지 체크한다. 
 * 
 * @author jinsuk, heesung 
 * 
 */
@Service
public class AuthenticationDelegator {
	private final Logger logger = LoggerFactory.getLogger(this.getClass());
	
	@Autowired
	private RememberMeCookieBaker cookieBaker;
	@Autowired 
	private MPlusApiCallService apiCallService; 
	@Autowired 
	private MPlusConfigureService configureService; 

/*	@Autowired
	private UserAuthService userAuthService;
	@Autowired
	private UserAttrService userAttrService;*/
	
	@Autowired
	private MPUserService userService;
	
	private HashUtil hashUtils; 
	
	@PostConstruct 
	public void afterPropertiesSet() { 
		this.hashUtils = new HashUtil(this.configureService.getHashSalt()); 
	}
	
	
	/**
	 * 로그인 처리 
	 * @param request
	 * @param response
	 * @param userId
	 * @param plainPassWord
	 * @param forcedPage 
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("unchecked")
	public MPlusUser authentication(HttpServletRequest request, HttpServletResponse response, String userId, String plainPassWord, String forcedPage) throws Exception {
	
		Map<String, Object> reqMap = new HashMap<String, Object>();
		Map<String, Object> resultMap = null;
		Map<String, Object> legacyResultMap = null; 
		List<Map<String, Object>> userAttrList = null;
		
		// 요청 파라미터 map 만들기
		reqMap.put("userid", userId);
		reqMap.put("passwords", plainPassWord);
				
		// 파티마병원 서버에 로그인 요청하기
		try { 
			legacyResultMap = (Map<String, Object>) this.apiCallService.execute(MPlusApi.USER_LOGIN_LOGINSUBMIT, reqMap); 
		} 
		catch(ApiCallException ex) { 
			// 어쨌든 화면에서는 로그인 처리실패로 진행되어야할것임 
			throw new LoginException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", null);			
		}
				
		// 로그인 실패인 경우 걸러내기
		if( legacyResultMap == null ){
			throw new LoginException("mplus.auth.checkaccount", "아이디 또는 비밀번호를 다시 확인하세요", null);
		} else {
			// 파티마병원에서 로그인이 성공했을 때는 emplno에 userID가 셋팅되어서 내려옴
			Object returnedUserId = legacyResultMap.get("emplno");
			
			// 로그인 실패 1번 케이스 - userID가 유효하지 않을 때
			// 리턴값에 emplno 필드가 존재하지않음
			if(returnedUserId == null) {
				throw new LoginException("mplus.auth.checkaccount", "아이디 또는 비밀번호를 다시 확인하세요", null);
			}
			
			// 로그인 실패 2번케이스 - userID 유효하지만 비밀번호 틀렸을 때
			// 리턴값에 emplno 필드가 존재하지만 값이 빈 배열임 ([])
			if(!(returnedUserId instanceof String)) {
				// 자동로그인이지만 패스워드가 바뀌어서 로그인에 실패한 경우 기존 쿠키는 반드시 지워야 한다
				this.cookieBaker.remove(request, response);
				throw new LoginException("mplus.auth.checkaccount", "아이디 또는 비밀번호를 다시 확인하세요", null);
			}
		}
		
		// 사용자의 첫 페이지
		Object indexPage = legacyResultMap.get("mobile");
		if (indexPage instanceof JSONArray) {
			indexPage = null;
		}
		
		// 변환 
		final MPlusUser user = new MPlusUser(
											(String) legacyResultMap.get("emplno"), 
											(String) legacyResultMap.get("name"), 
											(String) legacyResultMap.get("dutplcedeptcd"),
											(String) legacyResultMap.get("dutplcedeptnm"),
											(String) legacyResultMap.get("jobkindcd"),
											(String) indexPage); 

		/*		final BigDecimal attrOrder = (BigDecimal) resultMap.get("useAttrOrder");
		user.setUseAttrOrder(ConvertUtil.convertLong(attrOrder));*/
		
		// mplus DB에 해당 사용자의 정보가 있는지 확인  
		try {
			resultMap = (Map<String, Object>) this.apiCallService.execute(MPlusApi.USER_USERINFO_GETLOGININFO, "userId", userId); 
		}
		catch(ApiCallException ex) { 
			// 어쨌든 화면에서는 로그인 처리실패로 진행되어야할것임 
			throw new LoginException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", null);			
		}
			
		// 정보가 없으면
		// 사용자 첫 로그인 - 사용자 정보 INSERT, 속성 정보 INSERT
		if( resultMap == null ) {
			try {
				this.logger.debug("MPLUS 사용자로 등록되어 있지 않음: " + userId);
				userService.insertUser(userId, user.getUserName(), this.hashUtils.sha256(plainPassWord));
				
			} catch (MPlusServiceException e) {
				throw new LoginException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", null);
			}
		}
		
		try {
			userAttrList = (List<Map<String, Object>>) this.apiCallService.execute(MPlusApi.USER_USERINFO_GETUSERATTRLIST, "userId", userId);
			
			if(userAttrList.size() == 0) {
				
				Map<String, Object> attrReqMap = new HashMap<String, Object>();
				attrReqMap.put("useAttrOrder", 1);
				attrReqMap.put("userId", userId);
				
				this.apiCallService.execute(MPlusApi.USER_USERINFO_ADDUSERATTR, "userId", userId);
				this.apiCallService.execute(MPlusApi.USER_USE_ATTR_ORDER_UPDATE, attrReqMap);
			}
		} 
		catch(ApiCallException ex) { 
			throw new LoginException("mplus.error.if.system", "시스템 호출 오류가 발생했습니다 (IF)", null);			
		}
		
		// 세션에 사용자 정보 심기 
		final HttpSession session = request.getSession();
		// 사용자 ID 
		session.setAttribute(FrameworkConstants.SESSION.USER_ID.getKey(), userId);
		// 사용자 이름
		session.setAttribute(FrameworkConstants.SESSION.USER_NAME.getKey(), user.getUserName());
		// 부서 코드
		session.setAttribute(MPlusConstants.SESSION.DEPT_CODE.getKey(), user.getDeptCode());
		// 부서 이름
		session.setAttribute(MPlusConstants.SESSION.DEPT_NAME.getKey(), user.getDeptName());
		// 인덱스 페이지 설정 값
		session.setAttribute(MPlusConstants.SESSION.INDEX_PAGE.getKey(), user.getIndexPage());
		// 인덱스 페이지 설정 값
		session.setAttribute(MPlusConstants.SESSION.JOB_KIND_CD.getKey(), user.getJobKindCd());
		
		// 사용자가 선택한 속성이 없는 경우
/*		if(user.getUseAttrOrder() == 0l) {
			throw new UserAttrException("mplus.auth.not.select.userattr", "선택된 사용자 속성이 없습니다. 속성을 선택하세요.", FrameworkConstants.URI_SPECIAL_PAGE.USER_AUTH_ATTR);
		}
		else {
			//사용자 권한 목록 가져와서 session에 담기
			List<UserAuth> userAuths = this.userAuthService.getAuthAttrListByAuthId(userId, user.getUseAttrOrder());
			session.setAttribute(FrameworkConstants.SESSION.USER_AUTH_LIST.getKey(), userAuths);
		}*/
		
		// 사용자가 자기 손으로 순순히 로그인 하는 경우에는 대부분 index page로 가야 하지만 
		// 로그인하지 않은 상태에서 push를 받고, 로그인 한 다음에 어디로 가는 경우라던지 (2016.2.2.현재. 아직 고려되지 않고 있는 서비스 경로임) 
		// 로그인 된 상태에서 세션이 끊어졌는데 로그인이 필요한 어떤메뉴(예:번호표발급)를 누른 경우 자동로그인을 탄 다음에 해당 메뉴로 곧장 가기 위해 필요함 
		if(forcedPage != null) {
			// 이름은 page이지만, 사실 json요청일수도 있음 
			user.setNextPage(forcedPage);
			// index로 보낼려면 이렇게.... 
			//user.setNextPage(MPlusConstants.URI_MP_INDEX);
		}

		return user;
	}
	
	/**
	 * 패스워드 변경이 필요한지 여부 
	 * @param user
	 * @return
	 */
	private boolean checkPasswordValidDate(MPlusUser user) { 
		final long currTimeStamp = System.currentTimeMillis(); 
		final Calendar c = Calendar.getInstance(); 
		// 사용자가 암호를 설정한 날로 초기화한 다음에 
		c.setTimeInMillis(user.getPassWordUpdateDt().getTime());
		// 암호가 유효한 날짜수를 더하고 
		c.set(Calendar.DATE, c.get(Calendar.DATE) + this.configureService.getPasswordValidDate());
		
		// 날짜비교 
		if(currTimeStamp >= c.getTimeInMillis()) {
			// 패스워드 변경 필요 
			return true; 
		}
		
		// 패스워드 변경 필요 없음 
		return false; 
	}
}