package com.style24.front.biz.thirdparty;

import com.gagaframework.web.parameter.GagaMap;
import com.gagaframework.web.util.GagaFileUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.JsonObject;
import com.google.i18n.phonenumbers.PhoneNumberUtil;
import com.google.i18n.phonenumbers.Phonenumber;
import com.style24.core.support.env.TscConstants;
import com.style24.front.support.security.session.TsfSession;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.env.Environment;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Component;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.net.URI;
import java.util.Locale;


/**
 * 카카오 로그인
 *
 * @author jsshin
 * @since 2021. 02. 05
 */
@Component
@Slf4j
public class KaKaoLogin {

	@Autowired
	private Environment env;

	@Autowired
	private RestTemplate restTemplate;

	@Value("${has-ssl}")
	private String hasSsl;

	private String callBackUrl;
	private String restApiKey;
	private String profiles;
	private String tokenUrl;
	private String userInfoUrl;
	private String authorizeUrl;
	private String unlinkUrl;
	private String protocal;

	@PostConstruct
	public void init() {
		callBackUrl = env.getProperty("kakao.login.callbackUrl");
		restApiKey = env.getProperty("kakao.restApiKey");
		profiles = env.getProperty("spring.profiles.active").toLowerCase();
		tokenUrl = env.getProperty("kakao.tokenUrl");
		userInfoUrl = env.getProperty("kakao.userInfoUrl");
		authorizeUrl = env.getProperty("kakao.authorizeUrl");
		unlinkUrl = env.getProperty("kakao.unlinkUrl");

		boolean isSslServer = Boolean.parseBoolean(hasSsl);

		if (isSslServer) {
			protocal = "https://";
		} else {
			protocal = "http://";
		}

		log.debug("\n\n---- Kakao initialization started ----");
		log.debug("callBackUrl: [{}]", callBackUrl);
		log.debug("restApiKey: [{}]", restApiKey);
		log.debug("profiles: [{}]", profiles);
		log.debug("tokenUrl: [{}]", tokenUrl);
		log.debug("userInfoUrl: [{}]", userInfoUrl);
		log.debug("authorizeUrl: [{}]", authorizeUrl);
		log.debug("\n--- Kakao initialization completed ----\n");
	}

	/**
	 * 카카오 로그인 창
	 * @param state - 콜백 시 해당 값으로 비교 및 모바일을 redirect 값이 있음
	 * @return String - 호출 url정보
	 * @author jsshin
	 * @since 2021. 02. 05
	 */
	public String getAuthorizeUrl(String state) {
		StringBuilder apiUrlBuilder = new StringBuilder();
		String redirectUri = GagaFileUtil.getConcatenationPath(protocal + TsfSession.getHttpServletRequest().getServerName(), callBackUrl);
		apiUrlBuilder.append(authorizeUrl)
				.append("?client_id=")
				.append(restApiKey)
				.append("&redirect_uri=")
				.append(redirectUri)
				.append("&response_type=code&state=")
				.append(state);
		log.info("apiUrlBuilder ===> {}", apiUrlBuilder.toString());
		return apiUrlBuilder.toString();
	}

	/**
	 * 카카오 전달 받은 코드 값으로 토근값을 받아옴(API)
	 *
	 * @param code - 카카오에서 전달 해준 값
	 * @param state - 콜백 시 해당 값으로 비교 및 모바일을 redirect 값이 있음
	 * @return GagaMap - 통신으로 받아온 정보
	 * @author jsshin
	 * @since 2021. 02. 05
	 */
	public GagaMap getAccessTocken(String code, String state) {
		GagaMap resultMap = new GagaMap();
		String redirectUri = GagaFileUtil.getConcatenationPath(protocal + TsfSession.getHttpServletRequest().getServerName(), callBackUrl);
		String requestGb = "";
		try {
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
			params.add("grant_type", "authorization_code");
			params.add("client_id", restApiKey);
			params.add("redirect_uri", redirectUri);
			params.add("code", code);
			params.add("state", state);

			// state 값에 리다이렉트 url 같이 넘겨줌
			if (StringUtils.isNotBlank(state)) {
				String[] stateArr = StringUtils.split(state, "!@!");
				requestGb = stateArr[1];
			}

			// Header
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

			HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
			URI url = URI.create(tokenUrl);

			// POST방식으로 호출
			ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, request, String.class);
			log.info("responseEntity.getStatusCode(): {} ", responseEntity.getStatusCode());

			String jsonResult = responseEntity.getBody();
			log.info("responseEntity.getBody(): {} ", jsonResult);

			Gson gson = new GsonBuilder().create();
			resultMap = gson.fromJson(jsonResult, GagaMap.class); //access_token, refresh_token
			resultMap.setString("requestGb", requestGb);

		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return resultMap;
	}

	/**
	 * 카카오 사용자 정보
	 *
	 * @param accessToken - 해당 값으로 사용자 정보를 가져옴
	 * @return GagaMap - 사용자 정보
	 * @author jsshin
	 * @since 2021. 02. 05
	 */
	public GagaMap getKakaoUserInfo(String accessToken) {
		GagaMap resultMap = new GagaMap();
		try {
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();

			// Header
			HttpHeaders headers = new HttpHeaders();
			headers.set("Authorization", "Bearer " + accessToken);
			headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
			HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
			URI url = URI.create(userInfoUrl);

			// POST방식으로 호출
			ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, request, String.class);

			log.info("responseEntity.getStatusCode(): {} ", responseEntity.getStatusCode());

			String jsonResult = responseEntity.getBody();
			log.info("responseEntity.getBody(): {} ", jsonResult);

			Gson gson = new GsonBuilder().create();

			JsonObject obj = gson.fromJson(jsonResult, JsonObject.class);
			GagaMap properties = gson.fromJson(obj.get("properties"), GagaMap.class);
			GagaMap kakaoAccount = gson.fromJson(obj.get("kakao_account"), GagaMap.class);

			Locale locale = Locale.KOREA;

			String snsId = obj.get("id").toString();
			String custNm = properties.getString("nickname");
			String email = kakaoAccount.getString("email");
			String birthYmd = kakaoAccount.getString("birthyear") + kakaoAccount.getString("birthday");
			String cellPhnno = getPhoneNumber(kakaoAccount.getString("phone_number"), locale.getCountry());

			String sexGb = "";
			if (StringUtils.isNotBlank(kakaoAccount.getString("gender"))) {
				sexGb = kakaoAccount.getString("gender").equals("male") ? TscConstants.Gender.MALE.value() : TscConstants.Gender.FEMALE.value();
			}

			String birthSm = "";
			if (StringUtils.isNotBlank(kakaoAccount.getString("birthday_type"))) {
				birthSm = kakaoAccount.getString("birthday_type").equals("SOLAR") ? "S":"L";
			}
			String ci = kakaoAccount.getString("ci");

			resultMap.setString("snsId", snsId);
			resultMap.setString("custNm", custNm);
			resultMap.setString("email", email);
			resultMap.setString("ci", ci);
			resultMap.setString("cellPhnno", cellPhnno);
			resultMap.setString("birthYmd", birthYmd);
			resultMap.setString("birthSm", birthSm);
			resultMap.setString("sexGb", sexGb);

		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return resultMap;
	}

	/**
	 * 카카오 access_token 새로 발급
	 *
	 * @param refreshToken - 해당 값으로 사용자 정보를 가져옴
	 * @return GagaMap - access_token 값을 준다.
	 * @author jsshin
	 * @since 2021. 02. 05
	 */
	public GagaMap getRefreshTocken(String refreshToken) {
		GagaMap resultMap = new GagaMap();
		try {
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
			params.add("grant_type", "refresh_token");
			params.add("client_id", restApiKey);
			params.add("refresh_token", refreshToken);

			// Header
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

			HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
			URI url = URI.create(tokenUrl);

			// POST방식으로 호출
			ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, request, String.class);
			log.info("responseEntity.getStatusCode(): {} ", responseEntity.getStatusCode());

			String jsonResult = responseEntity.getBody();
			log.info("responseEntity.getBody(): {} ", jsonResult);

			Gson gson = new GsonBuilder().create();
			resultMap = gson.fromJson(jsonResult, GagaMap.class); //access_token
		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return resultMap;
	}

	/**
	 * 카카오 계정 연동해제
	 *
	 * @param accessToken - 토근값
	 * @return GagaMap - snsId 값이 있으면 연동해제 성공
	 * @author jsshin
	 * @since 2021. 02. 05
	 */
	public GagaMap saveUnlink(String accessToken) {
		GagaMap resultMap = new GagaMap();
		try {
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
			// Header
			HttpHeaders headers = new HttpHeaders();
			headers.set("Authorization", "Bearer " + accessToken);
			headers.add("Content-Type", "application/x-www-form-urlencoded;charset=utf-8");
			HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(params, headers);
			URI url = URI.create(unlinkUrl);

			// POST방식으로 호출
			ResponseEntity<String> responseEntity = restTemplate.postForEntity(url, request, String.class);
			log.info("responseEntity.getStatusCode(): {} ", responseEntity.getStatusCode());

			String jsonResult = responseEntity.getBody();
			log.info("responseEntity.getBody(): {} ", jsonResult);

			Gson gson = new GsonBuilder().create();
			JsonObject obj =  gson.fromJson(jsonResult, JsonObject.class);

			resultMap.setString("snsId", obj.get("id").toString());

		} catch (Exception e) {
			log.error(e.getMessage());
		}
		return resultMap;
	}

	/**
	 * 국가번호 붙여서 넘겨온 전화번호 변호
	 * +82) 010 1234 5678 -> 010-1234-5678
	 * @param cellPhnno - 휴대전화번호
	 * @param country - 국가
	 * @return String - 010-1234-5678
	 * @author jsshin
	 * @since 2021. 02. 05
	 */
	public String getPhoneNumber(String cellPhnno, String country) throws Exception {
		String result = "";
		if (StringUtils.isNotBlank(cellPhnno)) {
			PhoneNumberUtil phoneUtil = PhoneNumberUtil.getInstance();
			Phonenumber.PhoneNumber numberProto = phoneUtil.parse(cellPhnno, "KR");
			result = phoneUtil.format(numberProto, PhoneNumberUtil.PhoneNumberFormat.NATIONAL);
		}

		return result;
	}

}
