
package nlib.user.service.impl;

import java.util.HashMap;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;

import egovframework.com.ext.oauth.service.OAuthUniversalUser;
import nlib.cmm.session.SessionConfig;
import nlib.restful.service.DataApiReqVO;
import nlib.restful.service.DataApiResVO;
import nlib.user.service.LoginService;
import nlib.user.service.NlibLoginVO;
import nlib.util.StringUtil;

@Service("loginService")
public class LoginServiceImpl implements LoginService
{
	private static final Logger log = LoggerFactory.getLogger(LoginServiceImpl.class);
	
	@Autowired
	private AuthenticationManager authenticationManager;
	
	@Resource(name="loginDAO")
	private LoginDAO loginDAO;

	/* 
	 * 입력된 로그인정보를 받아 로그인 처리한다.
	 * 정상인 경우 null을 리턴하고, 오류발생시 오류코드를 리턴한다
	 * 
	 * (non-Javadoc)
	 * @see nlib.user.service.LoginService#login(javax.servlet.http.HttpServletRequest, java.lang.String, java.lang.String)
	 */
	public String login(HttpServletRequest req, String username, String password)  {
		
		// 아이디와 패스워드로, Security 가 알아 볼 수 있는 token 객체로 변경한다.
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, password);
		
        NlibLoginVO userVO = loginProcSecurity(req, token);
        
        if(StringUtil.isNotEmpty(userVO.getError())) {
        	return userVO.getError();
        }
        
        // 로그인 로그 정보 저장
        loginDAO.insertLoginLog(userVO);
        
		return null;
	}
	


	/* 
	 * SNS 연동 SSO를 처리를 위한 확인작업을 수행한다.
	 * (nlib에서 호출됨) 
	 * 
	 * return값이 null인 경우, 정상적인 로그인 가능 상태
	 * null이 아닌 경우, 오류 코드 
	 * - ERR_CODE_REQ_SNSLOGIN : SNS 정상적인 로그인이 필요    
	 * - ERR_CODE_REQ_MEMBERSHIP : 신규회원가입/회원통합 필요
	 */
	public String loginOauth(HttpServletRequest req, String oauthServiceName, String snsId, String joinCouncilCd, String councilJsessionId)  {
		
		if(StringUtil.isEmpty(snsId)) return ERR_CODE_REQ_SNSLOGIN;
		
		// SNS 연동 사용자UID로 사용자정보 조회 
		NlibLoginVO nLoginParamVO = new NlibLoginVO();
		nLoginParamVO.setSnsId(snsId);
		nLoginParamVO.setSnsType(oauthServiceName);
		nLoginParamVO.setJoinCouncilCd(joinCouncilCd);
		NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(nLoginParamVO);
		
		if(nLoginVO == null) {
			return ERR_CODE_REQ_MEMBERSHIP;
		}
		
		// 세션목록관리에 로그인 정상 가능 정보 추가 
		// TODO 
		//SessionConfig.setLoginInfo(councilJsessionId, o);
        
		// 회원가입정보 존재하는 SNS 아이디임을 확인 완료 
		return null;
	}
	
	/* 
	 * 지방문화원 해당 세션에 대한 최종 로그인 세션을 처리한다.
	 * 정상인 경우 null을 리턴하고, 오류발생시 오류코드를 리턴한다. 
	 * 
	 */
	public String loginCouncilSSO(HttpServletRequest req, String councilCd, OAuthUniversalUser oUser) {

		if(oUser == null) return ERR_CODE_REQ_SNSLOGIN;
		
		if(!oUser.isValid()) return ERR_CODE_REQ_SNSLOGIN;
		
		// SNS 연동 사용자UID로 사용자정보 조회 
		NlibLoginVO nLoginParamVO = new NlibLoginVO();
		nLoginParamVO.setSnsId(oUser.getSnsId());
		nLoginParamVO.setSnsType(oUser.getSnsType());
		nLoginParamVO.setJoinCouncilCd(councilCd);
		NlibLoginVO nLoginVO = loginDAO.selectLoginUserInfo(nLoginParamVO);
		
		if(nLoginVO == null) {
			return ERR_CODE_REQ_MEMBERSHIP;
		}

		// 휴면계정 확인 
		if("P".equals(nLoginVO.getStatus())) {
			oUser.setNlibVO(nLoginVO);
			return WRN_CODE_DORMANT;
		}
		
		// 탈퇴계정 확인 
		if("D".equals(nLoginVO.getStatus())) {
			oUser.setNlibVO(nLoginVO);
			return ERR_CODE_REQ_MEMBERSHIP;
		}
		
		// Spring Security 로그인처리위한 token 생성하여 로그인 처리 
        UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(nLoginVO.getUsername(), nLoginVO.getPassword());
        NlibLoginVO nlibLoginedVO = loginProcSecurity(req, token);

        if(StringUtil.isNotEmpty(nlibLoginedVO.getError())) {
        	return nlibLoginedVO.getError();
        }
        
        // 로그인 로그 정보 저장
        nlibLoginedVO.setIp(req.getRemoteAddr());
        loginDAO.insertLoginLog(nlibLoginedVO);
        
		return null;
	}
	
	/**
	 * 스프링 시큐리티 로그인 처리를 수행한다.
	 * 
	 * @param req
	 * @param token
	 * @return
	 */
	public NlibLoginVO loginProcSecurity(HttpServletRequest req, UsernamePasswordAuthenticationToken token)  {
		
		NlibLoginVO nlibLoginVO = null;
		
		try {
	        // AuthenticationManager 에 token 을 넘기면 UserDetailsService 가 받아 처리하도록 한다.
			Authentication authentication = authenticationManager.authenticate(token);
	        
	        // 기타 정보 저장 
			String sessionId = req.getSession().getId();
	        nlibLoginVO = (NlibLoginVO)authentication.getPrincipal();
	        nlibLoginVO.setIp(req.getRemoteAddr());
	        
	        // 지방문화원 정보 
	        HttpSession session = req.getSession();
	        nlibLoginVO.setJoinCouncilCd((String)session.getAttribute("councilCd"));
	        nlibLoginVO.setJoinCouncilNm((String)session.getAttribute("councilNm"));
	        
	        OAuthUniversalUser ousr = SessionConfig.getLoginInfo(sessionId);
	        nlibLoginVO.setSnsType(ousr.getSnsType());
	        
	        // 실제 SecurityContext 에 authentication 정보를 등록한다.
	        SecurityContextHolder.getContext().setAuthentication(authentication);
	        
	        SessionConfig.removeLoginInfo(sessionId);
	        
	        
		} catch (DisabledException e) {
			log.error("loginProcSecurity > " + ERR_CODE_LOCKED);
			if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
			nlibLoginVO.setError(ERR_CODE_LOCKED);
	    } catch (LockedException e) {
	    	log.error("loginProcSecurity > " + ERR_CODE_DISABLE);
			if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
			nlibLoginVO.setError(ERR_CODE_DISABLE);
	    } catch (BadCredentialsException e) {
	    	log.error("loginProcSecurity > " + ERR_CODE_MISMATCH);
			if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
			nlibLoginVO.setError(ERR_CODE_MISMATCH);
	    } catch (Exception e) {
	    	log.error("loginProcSecurity > " + ERR_CODE_EXC + " : " + e.toString());
			if(nlibLoginVO == null) nlibLoginVO = new NlibLoginVO();
			nlibLoginVO.setError(ERR_CODE_EXC + " : " + e.toString());
	    }
		
		return nlibLoginVO;
	}

	public DataApiResVO logout(DataApiReqVO reqVO)  {
		return null;
	}
	
	/* 
	 * DNS 호스트명으로 지방문화원 정보를 조회한다. (지방문화원코드, 명칭) 
	 */
	public HashMap<String, String> selectCouncilInfoByDnsHost(String councilDnsHost) {
		return loginDAO.selectCouncilInfoByDnsHost(councilDnsHost);
	}

	public int activateDormantAcc(NlibLoginVO loginVO) {
		return loginDAO.activateDormantAcc(loginVO);
	}
	
	public NlibLoginVO selectLoginUserInfo(NlibLoginVO loginVO) {
		return loginDAO.selectLoginUserInfo(loginVO);
	}
	
}