package com.bb.front;

import java.net.SocketTimeoutException;
import java.text.SimpleDateFormat;
import java.util.HashMap;

import org.apache.http.conn.ConnectTimeoutException;
import org.codehaus.jettison.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.ResourceAccessException;

import com.bb.model.ThenutResponse;
import com.bb.service.AsyncSiteService;
import com.bb.service.CallBackService;
import com.bb.service.SiteService;

import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import jakarta.servlet.http.HttpServletRequest;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RequestMapping("/api/cb/thenut")
@RequiredArgsConstructor
@SecurityRequirement(name = "Authorization")
@EnableScheduling
@RestController
public class CallBackThenutController {

	@Autowired
	SiteService siteService;

	@Autowired
	CallBackService callBackService;

	@Autowired
	AsyncSiteService asyncCallBackService;
	
	
	@ResponseBody
	@RequestMapping(path = "/balance")
	public ResponseEntity<ThenutResponse> balance(HttpServletRequest request, @RequestParam("username") String thenutUsername) throws Exception {
		ThenutResponse response = null;

		long siteIdx = 0L;
		try {
			siteIdx = Long.parseLong(thenutUsername.substring(0, 3), 16);
		} catch (NumberFormatException e) {
		    log.error("#-CB::THENUT::balance::Invalid username format (hex expected): {}", thenutUsername);
			response = new ThenutResponse();
			response.setBalance(0);
			response.setErrorCode(-1);
			response.setMessage("Invalid username format (hex expected)");
			return new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
		}
		String memberId = thenutUsername.substring(3, thenutUsername.length());
		final String LOG_PREFIX = "#-CB::THENUT::balance::"+memberId+":::";
		log.info(LOG_PREFIX+ "Request::" + thenutUsername);

		try {
			//사이트정보
			HashMap sParam = new HashMap();
			sParam.put("siteIdx", siteIdx);
			sParam.put("memberId", memberId);
			log.info(LOG_PREFIX + "SITE_API_INFO_PARAM::"+sParam.toString());
			HashMap siteApiInfo = siteService.getSiteApiInfo(sParam);
			if(siteApiInfo == null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_SITE_INFO");
				response = new ThenutResponse();
				response.setBalance(0);
				response.setErrorCode(-1);
				response.setMessage("NOT_FOUND_SITE_INFO");
				return new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
			}

			if(siteApiInfo.get("memberIdx") == null) {
				log.error(LOG_PREFIX+ "존재하지 않는 회원");
				response = new ThenutResponse();
				response.setBalance(0);
				response.setErrorCode(-1);
				response.setMessage("존재하지 않는 회원");
				return new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
			}

			// ##--isTransfer 처리-##
			int balance = 0;
			if("N".equals(siteApiInfo.get("isTransfer").toString())) {
				log.info(LOG_PREFIX + "PLAY_SEAMLESS");
				JSONObject member = new JSONObject();
				member.put("userId", memberId);
				log.info(LOG_PREFIX+ "balance Req ::"+ member.toString());
				JSONObject resData  = callBackService.getBalance(siteApiInfo, member);
				if(resData == null) {
					log.error(LOG_PREFIX+ "Error Msg: BALANCE_CALLBACK_ERROR");
					response = new ThenutResponse();
					response.setBalance(0);
					response.setErrorCode(-1);
					response.setMessage("BALANCE_CALLBACK_ERROR");
					return new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
				}
				log.info(LOG_PREFIX+ "balance Res ::"+ resData.toString());
				balance = resData.getInt("balance");
			} else {
				log.info(LOG_PREFIX + "PLAY_TRANSFER");
				HashMap balanceMap = new HashMap<>();
				balanceMap.put("siteIdx", siteIdx);
				balanceMap.put("memberId", memberId);
				balance = siteService.getUserBalance(balanceMap);

				if(siteApiInfo.get("siteCbUrl") != null && !"".equals(siteApiInfo.get("siteCbUrl").toString())) {
					JSONObject member = new JSONObject();
					member.put("userId", memberId);
					member.put("balance", balance);
					asyncCallBackService.asyncBalance(LOG_PREFIX, siteApiInfo, member);
				}
			}

			response = new ThenutResponse();
			response.setBalance(balance);
			response.setErrorCode(0);
			response.setMessage("SUCCESS");

		} catch (ResourceAccessException rae) {
            if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX+ "[ConnectTimeoutException]"+rae.getMessage());
            }

            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX+ "[SocketTimeoutException]"+rae.getMessage());
            }

            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX+ "[InterruptedException]"+rae.getMessage());
            }

            response = new ThenutResponse();
            response.setBalance(0);
            response.setErrorCode(-1);
            response.setMessage("TIMEOUT_ERROR");

		} catch (Exception e) {
			log.error(LOG_PREFIX+ "[Exception]"+e.toString());
			e.printStackTrace();
			response = new ThenutResponse();
			response.setBalance(0);
			response.setErrorCode(-9);
			response.setMessage("BALANCE_SERVER_ERROR");
		}

		ResponseEntity<ThenutResponse> res = new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
		log.info(LOG_PREFIX+ "Response::" + response.toString());

		return res;
	}
	
	
	@ResponseBody
	@RequestMapping(path = "/bet")
	public ResponseEntity<ThenutResponse> callBet(HttpServletRequest request, @RequestBody net.sf.json.JSONObject requestBody) throws Exception {
		ThenutResponse response = null;
		HttpStatus httpStatus = null;
		
		String thenutUsername = requestBody.getString("username");
		long siteIdx = 0L;
		try {
			siteIdx = Long.parseLong(thenutUsername.substring(0, 3), 16);
		} catch (NumberFormatException e) {
			log.error("#-CB::THENUT::callBet::Invalid username format (hex expected): {}", thenutUsername);
			response = new ThenutResponse();
			response.setBalance(0);
			response.setErrorCode(-1);
			response.setMessage("Invalid username format (hex expected)");
			return new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
		}
		String memberId = thenutUsername.substring(3, thenutUsername.length());
		long transactionId = requestBody.getLong("transactionId");
		String transactionType = requestBody.getString("transactionType");
		
		final String LOG_PREFIX = "#-CB::THENUT::callBet::"+memberId+"::"+transactionId+"::"+transactionType+":::";
		log.info(LOG_PREFIX+ "Request::" + requestBody.toString());
		
		//사이트정보
		HashMap sParam = new HashMap();
		sParam.put("siteIdx", siteIdx);
		sParam.put("memberId", memberId);
		sParam.put("vendor", "thenut");
		sParam.put("vendorGameCode", requestBody.getString("vendorCode"));
		log.info(LOG_PREFIX + "getSiteVendorInfo::"+sParam.toString());
		HashMap<String,String> siteVendorInfo = siteService.getSiteVendorInfo(sParam);
		if(siteVendorInfo == null) {
			response = new ThenutResponse();
			response.setErrorCode(-1);
			response.setMessage("존재하지 않는 회원입니다.");
			httpStatus = HttpStatus.OK;
			log.error(LOG_PREFIX + "Response Body: " + response.toString());
			return new ResponseEntity<ThenutResponse>(response, httpStatus);
		}
		
		// ##--isTransfer 처리-##
		final String IS_TRANSFER = siteVendorInfo.get("isTransfer").toString();
		
		try {
			// "processed_at":"2025-01-16T09:25:23.000000Z"
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
			String betDateTimeStr = requestBody.getString("betAt");
			log.info(LOG_PREFIX+ "========== BF_betDateTimeStr::" + betDateTimeStr);
			betDateTimeStr = betDateTimeStr.substring(0, 19);
			betDateTimeStr = betDateTimeStr.replace("T", " ");
			log.info(LOG_PREFIX+ "========== AF_betDateTimeStr::" + betDateTimeStr);
			
			int amount = requestBody.getInt("amount");
			String gameType = requestBody.getString("gameType");
			String gameId = requestBody.getString("gameCode");
			String gameName = requestBody.getString("gameTitle");
			
			String targetTableId = gameId.replaceAll(" ", "").toLowerCase();
			String banGameId = siteVendorInfo.get("banGameId").toString().replaceAll(" ", "").toLowerCase();
			if(!"".equals(targetTableId) && !"".equals(banGameId) && banGameId.contains(targetTableId+"|")) {
				log.error(LOG_PREFIX+ "Ban Game Id : " + banGameId);
				response = new ThenutResponse();
				response.setErrorCode(100);
				response.setMessage("Ban Game Id : " + banGameId);
				httpStatus = HttpStatus.OK;
				log.error(LOG_PREFIX + "Response Body: " + response.toString());
				return new ResponseEntity<ThenutResponse>(response, httpStatus);
			}
			
			String siteId = siteVendorInfo.get("siteId").toString();
			long memberIdx = Long.parseLong(siteVendorInfo.get("memberIdx").toString());
			String vendorApiId = siteVendorInfo.get("vendorApiId").toString();

			String tranId = Long.toString(transactionId);
			String betId = tranId;
			String betKey = requestBody.getString("gameRound")+requestBody.getString("gameCode")+requestBody.getString("vendorCode")+requestBody.getString("username");
			String vendorKey = siteVendorInfo.get("vendorKey").toString();
			int vendorIdx = Integer.parseInt(siteVendorInfo.get("vendorIdx").toString());
			String vendorCetegory = siteVendorInfo.get("vendorCetegory").toString();
			
			// ##--isTransfer 처리-##
			if(IS_TRANSFER.equals("Y")) {
				// tranId 중복체크
				int cntTr = siteService.getTranIdCheck2(tranId);
				if(cntTr > 0) {
					HashMap balanceMap = new HashMap<>();
					balanceMap.put("siteIdx", siteIdx);
					balanceMap.put("memberId", memberId);
					int balance = siteService.getUserBalance(balanceMap);
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(101);
					response.setMessage("이미 존재하는 트랜잭션");
					httpStatus = HttpStatus.OK;
					log.error(LOG_PREFIX + "Response Body: " + response.toString());
					return new ResponseEntity<ThenutResponse>(response, httpStatus);
				}
			}
			
			if(siteVendorInfo.get("lastInVendorIdx")==null || "0".equals(siteVendorInfo.get("lastInVendorIdx").toString())) {
				HashMap memParam = new HashMap<>();
				memParam.put("siteIdx", siteIdx);
				memParam.put("memberIdx", memberIdx);
				memParam.put("lastInVendorIdx", Integer.parseInt(siteVendorInfo.get("vendorIdx").toString()));
				int updResult = siteService.updMemLastVendorIdx(memParam);
			} else {
				int lastInVendorIdx = Integer.parseInt(siteVendorInfo.get("lastInVendorIdx").toString());
				if(lastInVendorIdx != vendorIdx) {
					log.error(LOG_PREFIX+ "VendorIdx doesn't match");
					response = new ThenutResponse();
					response.setErrorCode(-1);
					response.setMessage("VendorIdx doesn't match");
					httpStatus = HttpStatus.OK;
					log.error(LOG_PREFIX + "Response Body: " + response.toString());
					return new ResponseEntity<ThenutResponse>(response, httpStatus);
				}
			}
			
			
			long siteCredit = Long.parseLong(siteVendorInfo.get("credit").toString());
			// ##--isTransfer 처리-##
			if(siteCredit < amount && IS_TRANSFER.equals("N")) {
				response = new ThenutResponse();
				response.setErrorCode(-9);
				response.setMessage("NO_SITE_CREDIT");
				httpStatus = HttpStatus.OK;
				log.error(LOG_PREFIX + "Response Body: " + response.toString());
				return new ResponseEntity<ThenutResponse>(response, httpStatus);
			}
			
			// 최대 베팅 금액 처리 (userMaxBet 우선 적용)
			long siteMaxBet = Long.parseLong(siteVendorInfo.get("siteMaxBet").toString());
			long userMaxBet = 0;
			if(siteVendorInfo.get("userMaxBet") != null && !siteVendorInfo.get("userMaxBet").toString().isEmpty()) {
				try {
					userMaxBet = Long.parseLong(siteVendorInfo.get("userMaxBet").toString());
				} catch (NumberFormatException e) {
					userMaxBet = 0;
				}
			}

			// 최종 maxBet 결정: userMaxBet이 0이면 siteMaxBet 사용, 0이 아니면 userMaxBet 사용
			long finalMaxBet = (userMaxBet > 0) ? userMaxBet : siteMaxBet;
			log.info(LOG_PREFIX+ "MaxBet Info - siteMaxBet: " + siteMaxBet + ", userMaxBet: " + userMaxBet + ", finalMaxBet: " + finalMaxBet);

			if(finalMaxBet < amount) {
				log.error(LOG_PREFIX+ "MAX_BET_AMOUNT_OVER");
				log.error(LOG_PREFIX+ "finalMaxBet : " + finalMaxBet +", amount : " + amount);
				response = new ThenutResponse();
				response.setErrorCode(-1);
				response.setMessage("MAX_BET_AMOUNT_OVER");
				httpStatus = HttpStatus.OK;
				log.error(LOG_PREFIX + "Response Body: " + response.toString());
				return new ResponseEntity<ThenutResponse>(response, httpStatus);
			}
			
			HashMap tranParam = new HashMap();
			tranParam.put("tranId", tranId);
			tranParam.put("refId",  betId);
			tranParam.put("siteIdx", siteIdx);
			tranParam.put("siteId", siteId);
			tranParam.put("memberIdx", memberIdx);
			tranParam.put("memberId", memberId);
			tranParam.put("vendorCetegory", vendorCetegory);
			tranParam.put("vendorIdx", vendorIdx);
			tranParam.put("vendorCode", "thenut");
			tranParam.put("vendorTranKey", betKey);
			tranParam.put("gameType", gameType);
			tranParam.put("gameId", gameId);
			tranParam.put("gameIdx", gameName);
			tranParam.put("tranType", "debit");
			tranParam.put("depositAmt", amount);
			tranParam.put("creditAmt", "0");
			tranParam.put("isCancel", "N");
			tranParam.put("isTie", "N");
			tranParam.put("apiStatus", 0);
			tranParam.put("vendorApiId", vendorApiId);
			
			JSONObject callBackObj = new JSONObject();
			callBackObj.put("betId", betId);
			callBackObj.put("tranId", tranId);
			callBackObj.put("betKey", betKey);
			callBackObj.put("userId", memberId);
			callBackObj.put("vendorIdx", vendorIdx);
			callBackObj.put("vendorKey", vendorKey);
			callBackObj.put("vendor", "thenut");
			callBackObj.put("gameIdx", vendorIdx);
			callBackObj.put("gameKey", gameName);
			callBackObj.put("gameId", gameId);
			callBackObj.put("gameType", gameType);
			callBackObj.put("tranType", "debit");
			callBackObj.put("debit", amount);
			callBackObj.put("credit", 0);
			callBackObj.put("isCancel", 0);
			callBackObj.put("isBonus", 0);
			callBackObj.put("isData", "N");
			callBackObj.put("requestAt", betDateTimeStr);
			
			// ##--isTransfer 처리-##
			if(IS_TRANSFER.equals("N")) {
				log.info(LOG_PREFIX + "PLAY_SEAMLESS");
				log.info(LOG_PREFIX + "-----INSERT_BET_START-----");
				siteService.commonBetinsert(tranParam);
				log.info(LOG_PREFIX + "-----INSERT_BET_END-----");

				log.info(LOG_PREFIX + "SEND_BET request body: " + callBackObj.toString());
				JSONObject resData = callBackService.changeBalance(LOG_PREFIX, siteVendorInfo, callBackObj);
				log.info(LOG_PREFIX + "SEND_BET status code: " + resData.getLong("result_code"));
				log.info(LOG_PREFIX + "SEND_BET response body: " + resData.toString());

				if(resData.getLong("result_code") == 0) {
					tranParam.put("balance", resData.getInt("balance"));
					tranParam.put("isTransfer", IS_TRANSFER);
					int res = siteService.updateCbApi(tranParam);
					log.info(LOG_PREFIX + "UPDATE_CALLBACK_API_STATUS END");

					int balance = resData.getInt("balance");
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(0);
					response.setMessage("SUCCESS");
					httpStatus = HttpStatus.OK;
				} else if(resData.getLong("result_code") == 98) {
					int balance = resData.getInt("balance");
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(101);
					response.setMessage("이미 존재하는 트랜잭션");
					httpStatus = HttpStatus.OK;
				} else if(resData.getLong("result_code") == 80) {
					int balance = resData.getInt("balance");
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(102);
					response.setMessage("잔고 부족");
					httpStatus = HttpStatus.OK;
				} else {
					String msg = "";
					if(resData.has("error_msg")) msg = resData.getString("error_msg");
	 				log.error(LOG_PREFIX+ msg);
					response = new ThenutResponse();
					response.setErrorCode(500);
					response.setMessage("알수없는 에러");
					httpStatus = HttpStatus.OK;
				}
			} else {
				log.info(LOG_PREFIX + "PLAY_TRANSFER");
				HashMap balanceMap = new HashMap<>();
				balanceMap.put("siteIdx", siteIdx);
				balanceMap.put("memberId", memberId);
				int balance = siteService.getUserBalance(balanceMap);
				int afBalance = balance - amount;

				if(balance < amount) {
					response.setBalance(balance);
					response.setErrorCode(102);
					response.setMessage("잔고 부족");
					httpStatus = HttpStatus.OK;
					log.error(LOG_PREFIX+ "Response::금액을 확인하여 주세요::" + response.toString());
					return new ResponseEntity<ThenutResponse>(response, httpStatus);
				}

				log.info(LOG_PREFIX + "-----INSERT_BET_START-----");
				siteService.commonBetinsert(tranParam);
				log.info(LOG_PREFIX + "-----INSERT_BET_END-----");

				tranParam.put("balance", afBalance);
				tranParam.put("preBalance", balance);
				tranParam.put("isTransfer", IS_TRANSFER);
				int res = siteService.updateCbApi(tranParam);
				log.info(LOG_PREFIX + "UPDATE_CALLBACK_API_STATUS END");

				response.setBalance(afBalance);
				response.setErrorCode(0);
				response.setMessage("SUCCESS");
				httpStatus = HttpStatus.OK;

				if(siteVendorInfo.get("siteCbUrl") != null && !"".equals(siteVendorInfo.get("siteCbUrl").toString())) {
					callBackObj.put("balance", afBalance);
					asyncCallBackService.asyncChangeBalance(LOG_PREFIX, siteVendorInfo, callBackObj);
				}
			}
			
		} catch (ResourceAccessException rae) {
            if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX + "ConnectTimeoutException: " + rae.getMessage());
            }
        
            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX + "SocketTimeoutException: " + rae.getMessage());
            }
        
            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX + "InterruptedException: " + rae.getMessage());
            }
            
            response = new ThenutResponse();
            response.setErrorCode(-1);
            response.setMessage("TIMEOUT_ERROR");
			httpStatus = HttpStatus.OK;
			
		} catch (Exception e) {
			log.error(LOG_PREFIX + "Exception: " + e.getMessage());
			response = new ThenutResponse();
			response.setErrorCode(-9);
			response.setMessage("BALANCE_SERVER_ERROR");
			httpStatus = HttpStatus.OK;
		}
		
		ResponseEntity<ThenutResponse> res = new ResponseEntity<ThenutResponse>(response, httpStatus);
		log.error(LOG_PREFIX + "Response Body: " + response.toString());
		
		return res;
	}
	
	
	@ResponseBody
	@RequestMapping(path = "/result")
	public ResponseEntity<ThenutResponse> callResult(HttpServletRequest request, @RequestBody net.sf.json.JSONObject requestBody) throws Exception {
		ThenutResponse response = null;
		HttpStatus httpStatus = null;
		
		String thenutUsername = requestBody.getString("username");
		long siteIdx = 0L;
		try {
			siteIdx = Long.parseLong(thenutUsername.substring(0, 3), 16);
		} catch (NumberFormatException e) {
			log.error("#-CB::THENUT::callResult::Invalid username format (hex expected): {}", thenutUsername);
			response = new ThenutResponse();
			response.setBalance(0);
			response.setErrorCode(-1);
			response.setMessage("Invalid username format (hex expected)");
			return new ResponseEntity<ThenutResponse>(response, HttpStatus.OK);
		}
		String memberId = thenutUsername.substring(3, thenutUsername.length());
		long transactionId = requestBody.getLong("transactionId");
		long refererId = requestBody.getLong("refererId");
		String transactionType = requestBody.getString("transactionType");
		
		final String LOG_PREFIX = "#-CB::THENUT::callResult::"+memberId+"::"+transactionId+"::"+transactionType+":::";
		log.info(LOG_PREFIX+ "Request::" + requestBody.toString());
		
		//사이트정보
		HashMap sParam = new HashMap();
		sParam.put("siteIdx", siteIdx);
		sParam.put("memberId", memberId);
		sParam.put("vendor", "thenut");
		sParam.put("vendorGameCode", requestBody.getString("vendorCode"));
		log.info(LOG_PREFIX + "getSiteVendorInfo::"+sParam.toString());
		HashMap<String,String> siteVendorInfo = siteService.getSiteVendorInfo(sParam);
		if(siteVendorInfo == null) {
			response = new ThenutResponse();
			response.setErrorCode(-1);
			response.setMessage("존재하지 않는 회원입니다.");
			httpStatus = HttpStatus.OK;
			log.error(LOG_PREFIX + "Response Body: " + response.toString());
			return new ResponseEntity<ThenutResponse>(response, httpStatus);
		}
		
		// ##--isTransfer 처리-##
		final String IS_TRANSFER = siteVendorInfo.get("isTransfer").toString();
		
		try {
			// "processed_at":"2025-01-16T09:25:23.000000Z"
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
			String betDateTimeStr = requestBody.getString("betAt");
			log.info(LOG_PREFIX+ "========== BF_betDateTimeStr::" + betDateTimeStr);
			betDateTimeStr = betDateTimeStr.substring(0, 19);
			betDateTimeStr = betDateTimeStr.replace("T", " ");
			log.info(LOG_PREFIX+ "========== AF_betDateTimeStr::" + betDateTimeStr);
			
			int amount = requestBody.getInt("amount");
			String gameType = requestBody.getString("gameType");
			String gameId = requestBody.getString("gameCode");
			String gameName = requestBody.getString("gameTitle");
			
			String siteId = siteVendorInfo.get("siteId").toString();
			long memberIdx = Long.parseLong(siteVendorInfo.get("memberIdx").toString());
			String vendorApiId = siteVendorInfo.get("vendorApiId").toString();

			String tranId = Long.toString(transactionId);
			String betId = Long.toString(refererId);
			String betKey = requestBody.getString("gameRound")+requestBody.getString("gameCode")+requestBody.getString("vendorCode")+requestBody.getString("username");
			String vendorKey = siteVendorInfo.get("vendorKey").toString();
			int vendorIdx = Integer.parseInt(siteVendorInfo.get("vendorIdx").toString());
			String vendorCetegory = siteVendorInfo.get("vendorCetegory").toString();
			
			// ##--isTransfer 처리-##
			if(IS_TRANSFER.equals("Y")) {
				// tranId 중복체크
				int cntTr = siteService.getTranIdCheck2(tranId);
				if(cntTr > 0) {
					HashMap balanceMap = new HashMap<>();
					balanceMap.put("siteIdx", siteIdx);
					balanceMap.put("memberId", memberId);
					int balance = siteService.getUserBalance(balanceMap);
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(101);
					response.setMessage("이미 존재하는 트랜잭션");
					httpStatus = HttpStatus.OK;
					log.error(LOG_PREFIX + "Response Body: " + response.toString());
					return new ResponseEntity<ThenutResponse>(response, httpStatus);
				}
				
				// 베팅 체크
				int cntBet = siteService.getBetIdCheck2(betId);
				if(cntBet == 0) {
					// 처리한 베팅 존재X
					log.error(LOG_PREFIX+ "처리한 베팅 존재X");
					response = new ThenutResponse();
					response.setErrorCode(101);
					response.setMessage("처리한 베팅 존재X");
					httpStatus = HttpStatus.OK;
					log.error(LOG_PREFIX + "Response Body: " + response.toString());
					return new ResponseEntity<ThenutResponse>(response, httpStatus);
				}
			}
			
			if(siteVendorInfo.get("lastInVendorIdx")==null || "0".equals(siteVendorInfo.get("lastInVendorIdx").toString())) {
				HashMap memParam = new HashMap<>();
				memParam.put("siteIdx", siteIdx);
				memParam.put("memberIdx", memberIdx);
				memParam.put("lastInVendorIdx", Integer.parseInt(siteVendorInfo.get("vendorIdx").toString()));
				int updResult = siteService.updMemLastVendorIdx(memParam);
			} else {
				int lastInVendorIdx = Integer.parseInt(siteVendorInfo.get("lastInVendorIdx").toString());
				if(lastInVendorIdx != vendorIdx) {
					log.error(LOG_PREFIX+ "VendorIdx doesn't match");
					response = new ThenutResponse();
					response.setErrorCode(-1);
					response.setMessage("VendorIdx doesn't match");
					httpStatus = HttpStatus.OK;
					log.error(LOG_PREFIX + "Response Body: " + response.toString());
					return new ResponseEntity<ThenutResponse>(response, httpStatus);
				}
			}
			
			HashMap tranParam = new HashMap();
			tranParam.put("tranId", tranId);
			tranParam.put("refId",  betId);
			tranParam.put("siteIdx", siteIdx);
			tranParam.put("siteId", siteId);
			tranParam.put("memberIdx", memberIdx);
			tranParam.put("memberId", memberId);
			tranParam.put("vendorCetegory", vendorCetegory);
			tranParam.put("vendorIdx", vendorIdx);
			tranParam.put("vendorCode", "thenut");
			tranParam.put("vendorTranKey", betKey);
			tranParam.put("gameType", gameType);
			tranParam.put("gameId", gameId);
			if(transactionType.equals("PROMO_WIN")) {
				tranParam.put("gameIdx", "TheNut Promo");
			} else {
				tranParam.put("gameIdx", gameName);
			}
			tranParam.put("tranType", "credit");
			tranParam.put("depositAmt", "0");
			tranParam.put("creditAmt", amount);
			if(transactionType.equals("CANCEL")) {
				tranParam.put("isCancel", "Y");
			} else {
				tranParam.put("isCancel", "N");
			}
			tranParam.put("isTie", "N");
			tranParam.put("apiStatus", 0);
			tranParam.put("vendorApiId", vendorApiId);
			
			JSONObject callBackObj = new JSONObject();
			callBackObj.put("betId", betId);
			callBackObj.put("tranId", tranId);
			callBackObj.put("betKey", betKey);
			callBackObj.put("userId", memberId);
			callBackObj.put("vendorIdx", vendorIdx);
			callBackObj.put("vendorKey", vendorKey);
			callBackObj.put("vendor", "thenut");
			callBackObj.put("gameIdx", vendorIdx);
			if(transactionType.equals("PROMO_WIN")) {
				callBackObj.put("gameKey", "TheNut Promo");
			} else {
				callBackObj.put("gameIdx", gameName);
			}
			callBackObj.put("gameId", gameId);
			callBackObj.put("gameType", gameType);
			callBackObj.put("tranType", "credit");
			callBackObj.put("debit", 0);
			callBackObj.put("credit", amount);
			if(transactionType.equals("CANCEL")) {
				callBackObj.put("isCancel", 1);
			} else {
				callBackObj.put("isCancel", 0);
			}
			if(transactionType.equals("PROMO_WIN")) {
				callBackObj.put("isBonus", 1);
			} else {
				callBackObj.put("isBonus", 0);
			}
			callBackObj.put("isData", "N");
			callBackObj.put("requestAt", betDateTimeStr);
			
			// ##--isTransfer 처리-##
			if(IS_TRANSFER.equals("N")) {
				log.info(LOG_PREFIX + "PLAY_SEAMLESS");
				log.info(LOG_PREFIX + "-----INSERT_RESULT_START-----");
				siteService.commonBetinsert(tranParam);
				log.info(LOG_PREFIX + "-----INSERT_RESULT_END-----");

				log.info(LOG_PREFIX + "SEND_RESULT request body: " + callBackObj.toString());
				JSONObject resData = callBackService.changeBalance(LOG_PREFIX, siteVendorInfo, callBackObj);
				log.info(LOG_PREFIX + "SEND_RESULT status code: " + resData.getLong("result_code"));
				log.info(LOG_PREFIX + "SEND_RESULT response body: " + resData.toString());

				if(resData.getLong("result_code") == 0) {
					tranParam.put("balance", resData.getInt("balance"));
					tranParam.put("isTransfer", IS_TRANSFER);
					int res = siteService.updateCbApi(tranParam);
					log.info(LOG_PREFIX + "UPDATE_CALLBACK_API_STATUS END");

					int balance = resData.getInt("balance");
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(0);
					response.setMessage("SUCCESS");
					httpStatus = HttpStatus.OK;
				} else if(resData.getLong("result_code") == 98) {
					int balance = resData.getInt("balance");
					response = new ThenutResponse();
					response.setBalance(balance);
					response.setErrorCode(101);
					response.setMessage("이미 존재하는 트랜잭션");
					httpStatus = HttpStatus.OK;
				} else {
					String msg = "";
					if(resData.has("error_msg")) msg = resData.getString("error_msg");
	 				log.error(LOG_PREFIX+ msg);
					response = new ThenutResponse();
					response.setErrorCode(500);
					response.setMessage("알수없는 에러");
					httpStatus = HttpStatus.OK;
				}
			} else {
				log.info(LOG_PREFIX + "PLAY_TRANSFER");
				HashMap balanceMap = new HashMap<>();
				balanceMap.put("siteIdx", siteIdx);
				balanceMap.put("memberId", memberId);
				int balance = siteService.getUserBalance(balanceMap);
				int afBalance = balance + amount;

				log.info(LOG_PREFIX + "-----INSERT_RESULT_START-----");
				siteService.commonBetinsert(tranParam);
				log.info(LOG_PREFIX + "-----INSERT_RESULT_END-----");

				tranParam.put("balance", afBalance);
				tranParam.put("preBalance", balance);
				tranParam.put("isTransfer", IS_TRANSFER);
				int res = siteService.updateCbApi(tranParam);
				log.info(LOG_PREFIX + "UPDATE_CALLBACK_API_STATUS END");

				response.setBalance(afBalance);
				response.setErrorCode(0);
				response.setMessage("SUCCESS");
				httpStatus = HttpStatus.OK;

				if(siteVendorInfo.get("siteCbUrl") != null && !"".equals(siteVendorInfo.get("siteCbUrl").toString())) {
					callBackObj.put("balance", afBalance);
					asyncCallBackService.asyncChangeBalance(LOG_PREFIX, siteVendorInfo, callBackObj);
				}
			}
			
		} catch (ResourceAccessException rae) {
            if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX + "ConnectTimeoutException: " + rae.getMessage());
            }
        
            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX + "SocketTimeoutException: " + rae.getMessage());
            }
        
            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX + "InterruptedException: " + rae.getMessage());
            }
            
            response = new ThenutResponse();
            response.setErrorCode(-1);
            response.setMessage("TIMEOUT_ERROR");
			httpStatus = HttpStatus.OK;
			
		} catch (Exception e) {
			log.error(LOG_PREFIX + "Exception: " + e.getMessage());
			response = new ThenutResponse();
			response.setErrorCode(-9);
			response.setMessage("BALANCE_SERVER_ERROR");
			httpStatus = HttpStatus.OK;
		}
		
		ResponseEntity<ThenutResponse> res = new ResponseEntity<ThenutResponse>(response, httpStatus);
		log.error(LOG_PREFIX + "Response Body: " + response.toString());
		
		return res;
	}
}
