package com.bb.front;

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

import jakarta.servlet.http.HttpServletRequest;

import org.apache.http.conn.ConnectTimeoutException;
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.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
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.service.CallBackService;
import com.bb.service.SiteService;

import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.sf.json.JSONObject;

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

	@Autowired
	SiteService siteService;
	
	@Autowired
	CallBackService callBackService;
	
	// https://oprvender.com/api/cb/gsoft/syncBalance
	// https://oprvender.com/api/cb/gsoft/balance
	// https://oprvender.com/api/cb/gsoft/changeBalance
	
	@ResponseBody
	@GetMapping(path = "/balance", produces = "application/json") // JSON만 생산	
	public ResponseEntity<JSONObject> balance(HttpServletRequest request, @RequestParam("username") String grandUserId) throws Exception {
		JSONObject resData  = new JSONObject();
		HttpStatus httpStatus = null;
		
		long siteIdx = Long.parseLong(grandUserId.substring(0, 3), 16);
		String memberId = grandUserId.substring(3, grandUserId.length());
		final String LOG_PREFIX = "#-CB::GRAND::balance::"+memberId+":::";
		
		try {
			//사이트정보
			HashMap sParam = new HashMap();
			sParam.put("siteIdx", siteIdx);
			sParam.put("memberId",  memberId);
			
			HashMap siteApiInfo = siteService.getSiteApiInfo(sParam);
			if(siteApiInfo==null) {
				log.error(LOG_PREFIX+ "Error Msg: NOT_FOUND_SITE_INFO");				
				return new ResponseEntity<JSONObject>(resData, HttpStatus.INTERNAL_SERVER_ERROR);
			}
			
			org.codehaus.jettison.json.JSONObject member = new org.codehaus.jettison.json.JSONObject();
			member.put("userId", memberId);
			log.info(LOG_PREFIX+ "Callback URL::" + siteApiInfo.get("siteCbUrl")+"/balance");
			log.info(LOG_PREFIX+ "Callback Param::" + member.toString());
		
			member.put("userId", memberId);			
			org.codehaus.jettison.json.JSONObject resBalancedData  = callBackService.getBalance(siteApiInfo, member);
			if(resBalancedData == null) {
				log.error(LOG_PREFIX+ "Error Msg: BALANCE_CALLBACK_ERROR");				
				return new ResponseEntity<JSONObject>(resData, HttpStatus.INTERNAL_SERVER_ERROR);
			}
			int balance = resBalancedData.getInt("balance");
			
			resData.put("balance", balance);
			httpStatus = HttpStatus.OK;
			
		} 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());
            }
            
            log.error(LOG_PREFIX+ "[ResourceAccessException]"+rae.getMessage());
			
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
			
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "[Exception]"+e.getMessage());
			
			httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
		}
		
		ResponseEntity<JSONObject> res = new ResponseEntity<JSONObject>(resData, httpStatus);
		log.info(LOG_PREFIX+ "Response: "+resData.toString());
		
		return new ResponseEntity<JSONObject>(resData, HttpStatus.OK);
	}
	
	
	@ResponseBody
	@PostMapping(path = "/change-balance", produces = "application/json") // JSON만 생산	
	public ResponseEntity<JSONObject> changebalance(HttpServletRequest request, @RequestBody net.sf.json.JSONObject requestBody) throws Exception {
		
		String grandUserId = requestBody.getString("username");
		JSONObject transaction =  requestBody.getJSONObject("transaction");
		String tranType = transaction.getString("type");
		long siteIdx = Long.parseLong(grandUserId.substring(0, 3), 16);
		String memberId = grandUserId.substring(3, grandUserId.length());
		final String LOG_PREFIX = "#-CB::GRAND::changeBalance::"+memberId+"::"+transaction.getString("id")+"::"+tranType+":::";
		log.info(LOG_PREFIX+ "Request Body: "+requestBody.toString());
		
		JSONObject resData  = new JSONObject();
		HttpStatus httpStatus = null;
		
		try {
			//사이트정보
			HashMap sParam = new HashMap();
			sParam.put("siteIdx", siteIdx);
			sParam.put("memberId",  memberId);
			sParam.put("vendor", "grand");
			sParam.put("vendorGameCode", "pragmatic_slot");
			HashMap siteApiInfo = siteService.getSiteVendorInfo(sParam);
			if(siteApiInfo==null) {
				log.error(LOG_PREFIX+ "siteApiInfo is null "+tranType+" "+grandUserId+"::"+ "Error Msg: NOT_FOUND_SITE_INFO");				
				return new ResponseEntity<JSONObject>(resData, HttpStatus.INTERNAL_SERVER_ERROR);
			}
			
			long amount = requestBody.getLong("amount");
			long overAmount =requestBody.getLong("over_amount");
			long memberIdx = Long.parseLong(siteApiInfo.get("memberIdx").toString());
			long vendorIdx = Long.parseLong(siteApiInfo.get("vendorIdx").toString());
			
			String siteId = siteApiInfo.get("siteId").toString();
			String vendorApiId = siteApiInfo.get("vendorApiId").toString();
			String vendorKey = siteApiInfo.get("vendorKey").toString();
			
			// 2025-06-10T07:12:54.355000Z
			SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSSSSS");
			String betDateTimeStr = transaction.getString("processed_at");
			log.info(LOG_PREFIX+ "========== BF_betDateTimeStr::" + betDateTimeStr);
			betDateTimeStr = betDateTimeStr.substring(0, 23);
			betDateTimeStr = betDateTimeStr.replace("T", " ");
			log.info(LOG_PREFIX+ "========== MF_betDateTimeStr::" + betDateTimeStr);
			betDateTimeStr = makeEdate(LOG_PREFIX, betDateTimeStr, 32400000);
			log.info(LOG_PREFIX+ "========== AF_betDateTimeStr::" + betDateTimeStr);
			
			if(amount < 0) {
				long siteCredit = Long.parseLong(siteApiInfo.get("credit").toString());
				if(siteCredit < amount) {
					log.error(LOG_PREFIX+ "SITE_IDX: "+siteIdx+" NO_SITE_CREDIT");
					return new ResponseEntity<JSONObject>(resData, HttpStatus.INTERNAL_SERVER_ERROR);
				}
				
				long siteMaxBet = Long.parseLong(siteApiInfo.get("siteMaxBet").toString());
				if(siteMaxBet < amount) {
					log.error(LOG_PREFIX+ "SITE_IDX: "+siteIdx+"MAX_BET_AMOUNT_OVER");
					return new ResponseEntity<JSONObject>(resData, HttpStatus.INTERNAL_SERVER_ERROR);
				}
			}
			
			String tranId = siteId+transaction.getString("id")+tranType; 
			String betId = "";
			//String vendorTranKey = transaction.getString("id");
			String vendorTranKey = "";
			
			String gameName = "";
			String gameId = "";
			String gameType = "slot";
			try {
				JSONObject gameDetail =  transaction.getJSONObject("details");
				JSONObject gameJson =  gameDetail.getJSONObject("game");
				try {
					gameId = gameJson.getString("game");
					gameName = gameJson.getString("game_name");
					vendorTranKey = gameJson.getString("round");
				} catch(Exception e){
					log.error(LOG_PREFIX+ "gameName is empty");
					gameName = gameId;
				}
				
				if("bet".equals(tranType)) {
					betId = siteId + transaction.getString("id")+memberIdx;
				} else if("win".equals(tranType) || "adjust".equals(tranType)) {
					betId = siteId + transaction.getString("referer_id")+memberIdx;
				} else {
					betId = siteId + transaction.getString("id")+memberIdx;
				}
				
			} catch(Exception e) {
				if("bet".equals(tranType)) {
					betId = siteId + transaction.getString("id")+memberIdx;
				} else if("win".equals(tranType) || "adjust".equals(tranType)) {
					betId = siteId + transaction.getString("referer_id")+memberIdx;
				} else {
					betId = siteId + transaction.getString("id")+memberIdx;
				}
				log.error(LOG_PREFIX+ "gameDetail is empty");
			}
						
			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", "SLOT");
			tranParam.put("vendorIdx", vendorIdx);
			tranParam.put("vendorCode", "grand");
			tranParam.put("vendorTranKey", vendorTranKey);
			tranParam.put("gameType", gameType);
			tranParam.put("gameId", gameId);
			tranParam.put("gameIdx", gameName);
			if(amount <0) {
				tranParam.put("tranType", "debit");
				tranParam.put("depositAmt", Math.abs(amount));
				tranParam.put("creditAmt", "0");
			} else {
				tranParam.put("tranType", "credit");
				tranParam.put("depositAmt", 0);
				tranParam.put("creditAmt", amount-overAmount);
			}
			//tranParam.put("isCancel", "N");
			if("cancel".equals(tranType)) {
				tranParam.put("isCancel", "Y");
			}else {
				tranParam.put("isCancel", "N");
			}
			tranParam.put("isTie", "N");
			tranParam.put("apiStatus", 0);
			tranParam.put("vendorApiId", vendorApiId);
			log.info(LOG_PREFIX+ "-----INSERT_BET_START-----");
			siteService.commonBetinsert(tranParam);
			log.info(LOG_PREFIX+ "-----INSERT_BET_END-----");
			
			org.codehaus.jettison.json.JSONObject callBackObj = new org.codehaus.jettison.json.JSONObject();
			callBackObj.put("betId", betId);
			callBackObj.put("tranId", tranId);
			callBackObj.put("userId", memberId);
			callBackObj.put("vendorIdx", vendorIdx);
			callBackObj.put("vendorKey", vendorKey);
			callBackObj.put("vendor", "grand");
			callBackObj.put("gameIdx", vendorIdx);
			callBackObj.put("gameKey", gameName);
			callBackObj.put("gameId", gameId);
			callBackObj.put("gameType", gameType);
			if(amount < 0) {
				callBackObj.put("tranType", "debit");
				callBackObj.put("debit", Math.abs(amount));
				callBackObj.put("credit", 0);
			} else {
				callBackObj.put("tranType", "credit");
				callBackObj.put("debit", 0);
				callBackObj.put("credit", amount-overAmount);
			}
			//callBackObj.put("isCancel", 0);
			
			if("cancel".equals(tranType)) {
				callBackObj.put("isCancel", 1);
			} else {
				callBackObj.put("isCancel", 0);
			}
			
			if("adjust".equals(tranType)) {
				callBackObj.put("isBonus", 1);
			} else {
				callBackObj.put("isBonus", 0);
			}
			callBackObj.put("requestAt", betDateTimeStr);
			log.info(LOG_PREFIX+ "SEND_BET request body: " + callBackObj.toString());
			org.codehaus.jettison.json.JSONObject resTranData = callBackService.changeBalanceSlot(LOG_PREFIX, siteApiInfo, callBackObj);
			log.info(LOG_PREFIX+ "SEND_BET response body: " + resTranData.toString());
			
			if(resTranData.getLong("result_code") == 0) {
				// 성공
				tranParam.put("balance", resTranData.getLong("balance"));
				int res = siteService.updateCbApi(tranParam);
				log.info(LOG_PREFIX+ "UPDATE_CALLBACK_API_STATUS END");
				
				long balance = resTranData.getLong("balance");
				resData.put("balance", balance);
				httpStatus = HttpStatus.OK;
				
			} else if(resTranData.getLong("result_code") == 98) {
				// 중복
				long balance = resTranData.getLong("balance");
				resData.put("balance", balance);
				httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
			} else {
				// 실패
				log.error(LOG_PREFIX+ "CALLBACK_ERROR");
				httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
			}
			
		} catch (ResourceAccessException rae) {
            if(rae.getCause() instanceof ConnectTimeoutException) {
            	log.error(LOG_PREFIX+  "::"+grandUserId+"::"+ "[ConnectTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof SocketTimeoutException) {
            	log.error(LOG_PREFIX+ "::"+grandUserId+"::"+ "[SocketTimeoutException]"+rae.getMessage());
            }
        
            if(rae.getCause() instanceof InterruptedException) {
            	log.error(LOG_PREFIX+ "::"+grandUserId+"::"+ "[InterruptedException]"+rae.getMessage());
            }
            
            log.error(LOG_PREFIX+  "::"+grandUserId+"::"+ "[ResourceAccessException]"+rae.getMessage());
			
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
			
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "#-Grand::::"+grandUserId+"::"+ "[Exception]"+e.getMessage());
			
			httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
		}
		
		ResponseEntity<JSONObject> res = new ResponseEntity<JSONObject>(resData, httpStatus);
		log.info(LOG_PREFIX+ "::"+grandUserId+"::"+ "Response: "+resData.toString());
		
		return new ResponseEntity<JSONObject>(resData, httpStatus);
	}
	
	@ResponseBody
	@GetMapping(path = "/detail", produces = "application/json") // JSON만 생산	
	public ResponseEntity<JSONObject> detail(HttpServletRequest request, @RequestParam("username") String grandUserId) throws Exception {
		JSONObject resData  = new JSONObject();
		HttpStatus httpStatus = null;
		
		long siteIdx = Long.parseLong(grandUserId.substring(0, 3), 16);
		String memberId = grandUserId.substring(3, grandUserId.length());
		final String LOG_PREFIX = "#-CB::GRAND::detail"+memberId+":::";
		
		try {
			httpStatus = HttpStatus.OK;
			
		} 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());
            }
            
            log.error(LOG_PREFIX+ "[ResourceAccessException]"+rae.getMessage());
			
            httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
			
		} catch (Exception e) {
			log.error(LOG_PREFIX+ "[Exception]"+e.getMessage());
			
			httpStatus = HttpStatus.INTERNAL_SERVER_ERROR;
		}
		
		ResponseEntity<JSONObject> res = new ResponseEntity<JSONObject>(resData, httpStatus);
		log.info(LOG_PREFIX+ "Response: "+resData.toString());
		
		return new ResponseEntity<JSONObject>(resData, HttpStatus.OK);
	}
	
	private static String makeEdate(String LOG_PREFIX, String paramDate, int flag) {
		// TODO Auto-generated method stub
		String eDate = "";
		SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS");
		try {
			Date date = sdf1.parse(paramDate);
			Calendar calendar = Calendar.getInstance();
			calendar.setTime(date);
			calendar.add(Calendar.MILLISECOND, flag);
			Date date2 = calendar.getTime();
			eDate = sdf2.format(date2);
		} catch(Exception e) {
			log.error(LOG_PREFIX+ "makeEdate::Exception::"+e.getMessage());
		}
		return eDate;
	}
}
