package com.bb.service;

import java.math.BigInteger;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Base64;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.UUID;

import org.codehaus.jettison.json.JSONException;
import org.codehaus.jettison.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.reactive.function.client.WebClientRequestException; 
import org.springframework.web.reactive.function.client.WebClientResponseException; 
import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.web.util.UriComponentsBuilder;

import com.bb.dao.SiteDao;
import com.bb.dao.TransDao;
import com.bb.model.BetParam;
import com.bb.model.NexusApiInfo;
import com.bb.model.NexusTrplVO;
import com.bb.model.TransSearchVO;
import com.bb.util.SHA256WithRSA;
import com.bb.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
public class TransServiceImpl implements TransService {

	@Autowired
	private TransDao transDao;
	
	@Autowired
	private SiteDao siteDao;
	
	@Autowired
	private WebClient webClient;

	@Override
	public int getTransBetListCnt(TransSearchVO search) {
		// TODO Auto-generated method stub
		return transDao.getTransBetListCnt(search);
	}

	@Override
	public List<HashMap<String, Object>> getTransBetList(TransSearchVO search) {
		List<HashMap<String, Object>> list = transDao.getTransBetList(search);
		
		if(search.getTopId() != null && !search.getTopId().equals("")) {
			for(HashMap<String, Object> item : list) {
				HashMap param = new HashMap<>();
				param.put("topId", search.getTopId());
				param.put("siteId", item.get("siteId").toString());
				List<HashMap> treeInfo = siteDao.getSiteTreeInfo(param);
				item.put("treeInfo", treeInfo);
			}
		}
		
		return list;
	}

	@Override
	public HashMap<String, Object> getTransBetListSum(TransSearchVO search) {
		// TODO Auto-generated method stub
		return transDao.getTransBetListSum(search);
	}

	@Override
	public int getTransBetFastListCnt(TransSearchVO search) {
		// TODO Auto-generated method stub
		return transDao.getTransBetFastListCnt(search);
	}

	@Override
	public List<HashMap<String, String>> getTransBetFastList(TransSearchVO search) {
		
		return transDao.getTransBetFastList(search);
	}

	@Override
	public HashMap<String, Object> getBetListItem(TransSearchVO search) {
		HashMap<String, Object> item = transDao.getBetListItem(search.getRefId());
		HashMap param = new HashMap<>();
		param.put("topId", search.getTopId());
		param.put("siteId", item.get("siteId").toString());
		List<HashMap> treeInfo = siteDao.getSiteTreeInfo(param);
		item.put("treeInfo", treeInfo);
		
		return item;
	}

	@Override
	public HashMap<String, String> getTransBetFastListSum(TransSearchVO search) {
		// TODO Auto-generated method stub
		return transDao.getTransBetFastListSum(search);
	}

	@Override
	public HashMap getBetInfoByBetId(BetParam param) {
		// TODO Auto-generated method stub
		return transDao.getBetInfoByBetId(param);
	}

	@Override
	public String getGsoftRoundIdByBetId(String betId) {
		// TODO Auto-generated method stub
		return transDao.getGsoftRoundIdByBetId(betId);
	}
	
	private HttpHeaders getInfinitiHeader(String LOG_PREFIX) {
		LOG_PREFIX = LOG_PREFIX + "getInfinitiHeader::";
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
		headers.setAcceptCharset(Arrays.asList(Charset.forName("UTF-8")));
		
		return headers;
	}

	@Override
	public JSONObject getInfinitiBetDetail(String LOG_PREFIX, String apiBaseUrl, JSONObject bodyObj) {
		JSONObject jsonObj = null;
		LOG_PREFIX = LOG_PREFIX + "getInfinitiBetDetail::";
		
		try {
			HttpHeaders headers = getInfinitiHeader(LOG_PREFIX);
			String apiUrl = apiBaseUrl + "/get/history/datafeed";
			
			log.info(LOG_PREFIX+ "ApiUrl::"+apiUrl);
			log.info(LOG_PREFIX+ "HttpHeaders::"+headers.toString());
			log.info(LOG_PREFIX+ "BodyJson::"+bodyObj);
			
			ResponseEntity<String> response = webClient.post()
				.uri(apiUrl)
				.headers(h -> h.addAll(headers))
				.bodyValue(bodyObj.toString())
				.retrieve()
				.toEntity(String.class)
				.block();
			
			jsonObj = new JSONObject(response.getBody());
			log.info(LOG_PREFIX+ "response::"+jsonObj.toString());
			
			
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "WebClientResponseException Body::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SocketTimeoutException::" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "InterruptedException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "WebClientRequestException::" + e.getMessage());
			}
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.getMessage());
			e.printStackTrace();
		}
		
		return jsonObj;
	}
	
	private HttpHeaders getNexusHeader(String LOG_PREFIX, NexusApiInfo apiInfo, String bodyJson) {
		LOG_PREFIX = LOG_PREFIX + "getNexusHeader::";
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
		headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
		headers.setAcceptCharset(Arrays.asList(Charset.forName("UTF-8")));
		
		String hashCode = getNexusHashCode(LOG_PREFIX, bodyJson, apiInfo.getAgentSecretKey());
		headers.set("hash", hashCode);
		headers.set("agent", apiInfo.getAgentId());
		log.info(LOG_PREFIX+ "HttpHeaders::"+headers.toString());
		
		return headers;
	}
	
	private String getNexusHashCode(String LOG_PREFIX, String body, String secretKey) {
		String hashCode = "";
		LOG_PREFIX = LOG_PREFIX + "getNexusHashCode::";
		try {
			// Json String Body + Secret Key
			String result = body + secretKey;
			// String result = "{\"username\":\"057007mptest01\",\"nickname\":\"007mptest01\",\"siteUsername\":\"057007mptest01\"}";
			log.info(LOG_PREFIX+ "Body + Secret Key::"+result);
			// SHA-256 Hash make
			MessageDigest digestObj = MessageDigest.getInstance("SHA-256");
			byte[] hash_data = digestObj.digest(result.getBytes("UTF-8"));
			
			// Base64 encoding
			hashCode = Base64.getEncoder().encodeToString(hash_data);
			log.info(LOG_PREFIX+ "hashCode::"+hashCode);
		} catch(Exception e) {
			log.error(LOG_PREFIX+ "Exception::"+e.getMessage());
		}
		
		return hashCode;
	}

	@Override
	public JSONObject getNexusBetDetail(String LOG_PREFIX, NexusApiInfo nexusApiInfo, String trxId) {
		JSONObject jsonObj = null;
		LOG_PREFIX = LOG_PREFIX + "getNexusBetDetail::";
		
		try {
			JSONObject bodyObj = new JSONObject();
			bodyObj.put("transactionKeys", trxId);
			String bodyJson = bodyObj.toString();
			log.info(LOG_PREFIX+ "bodyJson::" + bodyJson);
			
			HttpHeaders headers = getNexusHeader(LOG_PREFIX, nexusApiInfo, bodyJson);
			String apiUrl = nexusApiInfo.getApiBaseUrl() + "/transaction/detail";
			
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
			params.add("transactionKeys", trxId);
			
			ResponseEntity<String> response = webClient.post()
				.uri(apiUrl)
				.headers(h -> h.addAll(headers))
				.bodyValue(params)
				.retrieve()
				.toEntity(String.class)
				.block();
			
			jsonObj = new JSONObject(response.getBody());
			
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "WebClientResponseException Body::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SocketTimeoutException::" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "InterruptedException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "WebClientRequestException::" + e.getMessage());
			}
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.getMessage());
			e.printStackTrace();
		}
		
		return jsonObj;
	}

	@Override
	public JSONObject getNexusBetDetailUrl(String LOG_PREFIX, NexusApiInfo nexusApiInfo, String trxId) {
		JSONObject jsonObj = null;
		LOG_PREFIX = LOG_PREFIX + "getNexusBetDetailUrl::";
		
		try {
			JSONObject bodyObj = new JSONObject();
			bodyObj.put("transactionKey", trxId);
			bodyObj.put("lang", "ko");
			String bodyJson = bodyObj.toString();
			log.info(LOG_PREFIX+ "bodyJson::" + bodyJson);
			
			HttpHeaders headers = getNexusHeader(LOG_PREFIX, nexusApiInfo, bodyJson);
			String apiUrl = nexusApiInfo.getApiBaseUrl() + "/getdetailurl";
			
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
			params.add("transactionKey", trxId);
			params.add("lang", "ko");
			
			ResponseEntity<String> response = webClient.post()
				.uri(apiUrl)
				.headers(h -> h.addAll(headers))
				.bodyValue(params)
				.retrieve()
				.toEntity(String.class)
				.block();
			
			jsonObj = new JSONObject(response.getBody());
			
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "WebClientResponseException Body::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SocketTimeoutException::" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "InterruptedException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "WebClientRequestException::" + e.getMessage());
			}
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.getMessage());
			e.printStackTrace();
		}
		
		return jsonObj;
	}

	@Override
	public HashMap getSiteBetByRefIdFromOld(BetParam param) {
		// TODO Auto-generated method stub
		return transDao.getSiteBetByRefIdFromOld(param);
	}

	@Override
	public JSONObject getSvendorDetail(String LOG_PREFIX, HashMap betInfo) throws JSONException {
		JSONObject dataObj = null;
		
		//body
		MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
		parameters.add("trid", betInfo.get("uuid").toString());
		
		HttpHeaders header = new HttpHeaders(); 	
		header.set("Authorization", betInfo.get("vendorApiKey").toString());
		header.set("Content-Type", "application/x-www-form-urlencoded");
		
		log.info(LOG_PREFIX+ "SVENDOR:request url::" + betInfo.get("vendorApiUrl").toString() + "/transaction-single");
		log.info(LOG_PREFIX+ "SVENDOR:request data::" + parameters.toString());
		
		try {
			ResponseEntity<String> responseEntity = webClient.post()
				.uri(betInfo.get("vendorApiUrl").toString() + "/transaction-single")
				.headers(h -> h.addAll(header))
				.bodyValue(parameters)
				.retrieve()
				.toEntity(String.class)
				.block();
			
			JSONObject resJson = new JSONObject(responseEntity.getBody());
			int resCode = resJson.getInt("code");
			String resMsg = resJson.getString("msg");
			log.info(LOG_PREFIX+ "SVENDOR::Response::Code" + resCode);
			log.info(LOG_PREFIX+ "SVENDOR::Response::Message" + resMsg);
			if(resCode == 0) {
				dataObj = resJson.getJSONObject("data");
			}
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "SVENDOR:WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "SVENDOR:WebClientResponseException::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "SVENDOR:ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SVENDOR:SocketTimeoutException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "SVENDOR:WebClientRequestException::" + e.getMessage());
			}
		} catch (JSONException e) {
			log.error(LOG_PREFIX+ "SVENDOR:error::" + e.getLocalizedMessage());
			log.error(LOG_PREFIX+ "SVENDOR:error::" + e.getMessage());
			e.printStackTrace();
		}
		
		return dataObj;
	}

	@Override
	public HashMap<String, String> getApiInfo(String vendorTitle) {
		// TODO Auto-generated method stub
		return transDao.getApiInfo(vendorTitle);
	}

	@Override
	public int deleteEvoDetailNull(HashMap detailMap) {
		// TODO Auto-generated method stub
		return transDao.deleteEvoDetailNull(detailMap);
	}
	
	private HttpHeaders getCncpHeader(String LOG_PREFIX, HashMap<String, Object> apiInfo) {
		LOG_PREFIX = LOG_PREFIX + "::getCncpHeader::";
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
		headers.setAcceptCharset(Arrays.asList(Charset.forName("UTF-8")));
		
		String authorization = getCncpAuthorization(LOG_PREFIX, apiInfo);
		headers.set("Authorization", authorization);
		log.info(LOG_PREFIX+ "HttpHeaders::"+headers.toString());
		
		return headers;
	}
	
	
	private String getCncpAuthorization(String LOG_PREFIX, HashMap<String, Object> apiInfo) {
		String authorization = "";
		LOG_PREFIX = LOG_PREFIX + "::getCncpAuthorization::";
		try {
			String apiAgentId = apiInfo.get("apiAgentId").toString();
			String apiPrivateKey = apiInfo.get("apiPrivateKey").toString();
			String apiHttpVerb = apiInfo.get("apiHttpVerb").toString();
			String apiEndPoint = apiInfo.get("apiEndPoint").toString();
			long apiTimestemp = System.currentTimeMillis();
			UUID uuid = UUID.randomUUID();
			String apiNonceString = uuid.toString();
			
			String inputText = apiHttpVerb + " " + apiEndPoint + " " + apiTimestemp + " " + apiNonceString;
			log.info(LOG_PREFIX+ "inputText::"+inputText);
			String signText = SHA256WithRSA.sign(inputText, apiPrivateKey);
			log.info(LOG_PREFIX+ "signText::"+signText);
			
			authorization = "HERAPLAY-SHA256-RSA2048 " + apiAgentId+","+apiTimestemp+","+apiNonceString+","+signText;
			log.info(LOG_PREFIX+ "Authorization::"+authorization);
			
		} catch(Exception e) {
			log.error(LOG_PREFIX+ "Exception::"+e.getMessage());
		}
		
		return authorization;
	}

	@Override
	public JSONObject getCncpDetailsById(String LOG_PREFIX, HashMap<String, Object> apiInfo) {
		JSONObject result = null;
		LOG_PREFIX = LOG_PREFIX + "gameRoundDetailsById::";
		
		apiInfo.put("apiHttpVerb", "GET");
		apiInfo.put("apiEndPoint", "datafeedapi/gamerounddetailsbyid");
		
		try {
			HttpHeaders headers = getCncpHeader(LOG_PREFIX, apiInfo);
			String apiUrl = apiInfo.get("apiBaseUrl").toString() + "/DataFeedAPI/GameRoundDetailsById?";
			String param = "providerCode=" + apiInfo.get("providerCode").toString() + "&roundId=" + apiInfo.get("roundId").toString();
			apiUrl = apiUrl + param;
			log.info(LOG_PREFIX+ "Request URL : " + apiUrl);
			log.info(LOG_PREFIX+ "Request Header : " + headers.toString());
			
			ResponseEntity<String> resEntity = webClient.get()
				.uri(apiUrl)
				.headers(h -> h.addAll(headers))
				.retrieve()
				.toEntity(String.class)
				.block();
			
			if(resEntity != null && resEntity.getStatusCode() == HttpStatus.OK) {
				JSONObject responseObj = new JSONObject(resEntity.getBody());
				if(responseObj.has("result")) {
					result = responseObj.getJSONObject("result");
				} else {
					log.error(LOG_PREFIX+ "Response : Has not result");
				}
			} else {
				log.error(LOG_PREFIX+ "Response : unknown error");
			}
			
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "WebClientResponseException Body::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SocketTimeoutException::" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "InterruptedException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "WebClientRequestException::" + e.getMessage());
			}
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.getMessage());
			e.printStackTrace();
		}
		
		return result;
	}
	
	@Override
	public JSONObject getDetailByBetId(String LOG_PREFIX, HashMap<String, Object> apiInfo) {
		JSONObject result = null;
		LOG_PREFIX = LOG_PREFIX + "getDetailByBetId::";
		
		try {
			HttpHeaders headers = new HttpHeaders();
			headers.setContentType(MediaType.APPLICATION_JSON);
			headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
			headers.setAcceptCharset(Arrays.asList(Charset.forName("UTF-8")));
			headers.set("Authorization", apiInfo.get("apiAuthorization").toString());
			
			String apiUrl = apiInfo.get("apiBaseUrl").toString() + "/bet/" + apiInfo.get("betId").toString();
			log.info(LOG_PREFIX+ "Request URL : " + apiUrl);
			log.info(LOG_PREFIX+ "Request Header : " + headers.toString());
			
			ResponseEntity<String> resEntity = webClient.get()
				.uri(apiUrl)
				.headers(h -> h.addAll(headers))
				.retrieve()
				.toEntity(String.class)
				.block();
			
			if(resEntity != null && resEntity.getStatusCode() == HttpStatus.OK) {
				JSONObject responseObj = new JSONObject(resEntity.getBody());
				if(responseObj.has("detail")) {
					String detailStr = responseObj.getString("detail");
					JSONObject detailObj = new JSONObject(detailStr);
					result = detailObj.getJSONObject("detail").getJSONObject("data");
				} else {
					log.error(LOG_PREFIX+ "Response : Has not result");
				}
			} else {
				log.error(LOG_PREFIX+ "Response : unknown error");
			}
			
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "WebClientResponseException Body::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SocketTimeoutException::" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "InterruptedException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "WebClientRequestException::" + e.getMessage());
			}
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.getMessage());
			e.printStackTrace();
		}
		
		
		return result;
	}
	
	private String getAPI_KEY(String LOG_PREFIX, HashMap<String, String> apiInfo) {
		LOG_PREFIX = LOG_PREFIX + "::getAPI_KEY::";
		
		String apiKey = "";
		String apiAgentCode = apiInfo.get("apiAgentCode");
		String apiToken = apiInfo.get("apiToken");
		String timeStamp = apiInfo.get("timeStamp");
		
		log.info(LOG_PREFIX+ "apiAgentCode::"+apiAgentCode);
		log.info(LOG_PREFIX+ "apiToken::"+apiToken);
		log.info(LOG_PREFIX+ "timeStamp::"+timeStamp);
		
		String textA = apiAgentCode+apiToken+timeStamp;
		String salt = apiToken;
		String textB = textA+salt;
		log.info(LOG_PREFIX+ "textB::"+textB);
		
		try {
			MessageDigest md = MessageDigest.getInstance("SHA-256");
		    md.update(textB.getBytes("UTF-8"));
		    byte[] digest = md.digest();
		    apiKey = new BigInteger(1, digest).toString(16).toLowerCase();
		    log.info(LOG_PREFIX+ "apiKey::"+apiKey);
		} catch(Exception e) {
			log.error(LOG_PREFIX+ "Exception::"+e.getMessage());
		}
		
		return apiKey;
	}

	@Override
	public JSONObject getPracpDetail(String LOG_PREFIX, HashMap<String, String> apiInfo) {
		LOG_PREFIX = LOG_PREFIX + "getDetail::";
		JSONObject detailObj = null;
		
		HttpHeaders headers = new HttpHeaders();
		headers.setContentType(MediaType.APPLICATION_JSON);
		headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
		headers.setAcceptCharset(Arrays.asList(Charset.forName("UTF-8")));
		
		String apiUrl = apiInfo.get("apiBaseUrl").toString() + "/app/game/detail-url?";
		
		SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
		long timeStampL = System.currentTimeMillis();
		String timeStamp = sdf.format(timeStampL);
		apiInfo.put("timeStamp", timeStamp);
		String apiKey = getAPI_KEY(LOG_PREFIX, apiInfo);
		String param = "api_key=" + apiKey + "&timestamp=" + timeStamp
			+ "&agent_code=" + apiInfo.get("apiAgentCode") + "&uuid=" + apiInfo.get("uuid") + "&round_id=" + apiInfo.get("round_id") + "&country_code=KR&locale_code=ko";
		apiUrl = apiUrl + param;
		log.info(LOG_PREFIX+ "apiUrl::" + apiUrl);
		
		try {
			ResponseEntity<String> resEntity = webClient.get()
				.uri(apiUrl)
				.headers(h -> h.addAll(headers))
				.retrieve()
				.toEntity(String.class)
				.block();
			
			if(resEntity != null && resEntity.getStatusCode() == HttpStatus.OK) {
				JSONObject responseObj = new JSONObject(resEntity.getBody());
				log.info(LOG_PREFIX+ "Response : " + responseObj.toString());
				detailObj = responseObj.getJSONObject("data");
			}
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "WebClientResponseException::" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "WebClientResponseException Body::" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "ConnectTimeoutException::" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "SocketTimeoutException::" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "InterruptedException::" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "WebClientRequestException::" + e.getMessage());
			}
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.getMessage());
			e.printStackTrace();
		}
		
		return detailObj;
	}

	@Override
	public String getTplusDetails(String LOG_PREFIX, HashMap<String, String> apiInfo) {
		JSONObject resJson = null;
		String detailUrl = null;
		LOG_PREFIX = LOG_PREFIX + "tplus::getDetails::";
		
		try {
			HttpHeaders header = new HttpHeaders(); 
			header.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
			header.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
			header.set("Authorization", "Bearer " + apiInfo.get("apiAuthKey"));
			
			MultiValueMap<String, String> params = new LinkedMultiValueMap<>();
			params.add("pair_transaction_key", apiInfo.get("pairTransactionKey"));
			
			String apiUrl = apiInfo.get("apiBaseUrl") + "/Details";
			
			ResponseEntity<String> response = webClient.post()
				.uri(apiUrl)
				.headers(h -> h.addAll(header))
				.bodyValue(params)
				.retrieve()
				.toEntity(String.class)
				.block();
			
			resJson = new JSONObject(response.getBody());
			if(resJson.getInt("code") == 0) {
				log.info(LOG_PREFIX+ "message: " + resJson.getString("message"));
				detailUrl = resJson.getString("url");
				log.info(LOG_PREFIX+ "detailUrl: " + detailUrl);
			} else {
				log.error(LOG_PREFIX+ "code: " + resJson.getInt("code"));
				log.error(LOG_PREFIX+ "message: " + resJson.getString("message"));
			}
		} catch (WebClientResponseException e) {
			log.error(LOG_PREFIX+ "[WebClientResponseException]" + e.getStatusCode().value());
			log.error(LOG_PREFIX+ "[WebClientResponseException]" + e.getResponseBodyAsString());
		} catch (WebClientRequestException e) {
			Throwable cause = e.getCause();
			if (cause instanceof io.netty.channel.ConnectTimeoutException) {
				log.error(LOG_PREFIX+ "[ConnectTimeoutException]" + e.getMessage());
			} else if (cause instanceof io.netty.handler.timeout.ReadTimeoutException) {
				log.error(LOG_PREFIX+ "[SocketTimeoutException]" + e.getMessage());
			} else if (cause instanceof InterruptedException) {
				log.error(LOG_PREFIX+ "[InterruptedException]" + e.getMessage());
			} else {
				log.error(LOG_PREFIX+ "[WebClientRequestException]" + e.getMessage());
			}
		} catch(Exception e) {
			log.error(LOG_PREFIX+ "Exception::" + e.toString());
			e.printStackTrace();
		}
		
		return detailUrl;
	}
	
	private boolean hasCause(Throwable e, Class<? extends Throwable> clazz) {
	    Throwable cause = e;
	    while (cause != null) {
	        if (clazz.isInstance(cause)) {
	            return true;
	        }
	        cause = cause.getCause();
	    }
	    return false;
	}

	@Override
	public String getPragmaticDetails(String lOG_PREFIX, HashMap betInfo) {
		try {
            String historyUrl = betInfo.get("vendorApiUrl").toString() + "/IntegrationService/v3/http/HistoryAPI/OpenHistoryExtended/";
            String siteIdxStr = betInfo.get("siteHex").toString();
            String playerId = siteIdxStr.toUpperCase() + betInfo.get("memberId").toString();
            String refId = betInfo.get("refId").toString();
            String roundId = refId.replaceAll(betInfo.get("siteId").toString(), "");
            //body
	        MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
	       
	
	        parameters.add("secureLogin", betInfo.get("vendorApiId").toString());
	        parameters.add("playerId", playerId);
	        parameters.add("gameId", betInfo.get("gameIdx").toString());
	        parameters.add("roundId", roundId);

	        // hash
	        String hashString = "secureLogin="+ betInfo.get("vendorApiId").toString()
	                + "&playerId="+playerId
	                + "&gameId="+betInfo.get("gameIdx").toString()
	                + "&roundId="+ roundId;

	        log.info("Pragmatic hash string:{}", hashString);
	        parameters.add("hash", StringUtils.md5AndHex(hashString+ betInfo.get("vendorApiKey").toString()));
	        //header
	        
	        HttpHeaders header = new HttpHeaders();
	        header.add("Content-Type", "application/x-www-form-urlencoded");


	        ResponseEntity<String> responseEntity = webClient.post()
	        	.uri(historyUrl)
	        	.headers(h -> h.addAll(header))
	        	.bodyValue(parameters)
	        	.retrieve()
	        	.toEntity(String.class)
	        	.block();
	        	
	        JSONObject resdata  = new JSONObject(responseEntity.getBody());
	        		        
            if(resdata != null) {
                return resdata.getString("url");
            } else {
                return "";
            }
        } catch(WebClientResponseException e) {
            log.error("OpenHistoryExtended WebClientResponseException: {}", e.getStatusCode().value());
            log.error("OpenHistoryExtended WebClientResponseException Body: {}", e.getResponseBodyAsString());
            return "";
        } catch(WebClientRequestException e) {
            log.error("OpenHistoryExtended WebClientRequestException: ", e);
            return "";
        } catch(Exception e) {
            log.error("OpenHistoryExtended Exception: ", e);
            return "";
        }
	}
}
