package cokr.xit.ens.biz.iup.billpay.service.process;

import cokr.xit.ens.biz.iup.domain.*;
import cokr.xit.ens.biz.iup.domain.repository.TbIupBillPayHistRepository;
import cokr.xit.ens.biz.iup.domain.repository.TbNaverPayResultRepository;
import cokr.xit.ens.core.exception.EnsException;
import cokr.xit.ens.core.exception.code.EnsErrCd;
import cokr.xit.ens.core.utils.CmmnUtil;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.nv.service.process.model.NvPayResultDetail;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.nv.service.process.model.NvPayResultReqData;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.api.PayUseSysApi;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.code.PaidTypeCd;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.code.PayUseSysHisSeCd;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.code.PayUseSysResCd;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.model.PayApiRespDTO;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.service.process.kit.PayResultService;
import cokr.xit.ens.modules.common.domain.support.FieldError;
import com.google.gson.Gson;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Service
public class IupNvPayResultService implements PayResultService<Map<String, Object>, Map<String, Object>, PayApiRespDTO<Map<String, Object>>> {

    private final JPAQueryFactory query;
    private final PayUseSysApi payUseSysApi;
    private final TbNaverPayResultRepository tbNaverPayResultRepository;
    private final TbIupBillPayHistRepository tbIupBillPayHistRepository;

    private Gson gson = new Gson();

    private enum DataKeys {
        tbInputDataXit,
        responseJsonStr,
    }

    @Override
    public Map<String, Object> getData(Map<String, Object> reqParam) {
        Map<String, Object> body = (Map<String, Object>) reqParam.getOrDefault("body", new HashMap<>());
        Map<String, Object> detail = (Map<String, Object>) body.getOrDefault("detail", new HashMap<>());
        String billerUserKey = (String) detail.get("merchantUserKey");

        if (billerUserKey == null) billerUserKey = "";
        TbInputDataXit tbInputDataXit = query.selectFrom(QTbInputDataXit.tbInputDataXit)
                .innerJoin(QTbInputDataXit.tbInputDataXit.tbInputXit, QTbInputXit.tbInputXit).fetchJoin()
                .innerJoin(QTbInputXit.tbInputXit.fsJob, QFsJob.fsJob).fetchJoin()
                .where(QTbInputDataXit.tbInputDataXit.linkedUuid.eq(billerUserKey))
                .fetchOne();


        Map<String, Object> data = new HashMap<>();
        data.put(DataKeys.tbInputDataXit.name(), tbInputDataXit);
        return data;
    }

    @Override
    public void validate(Map<String, Object> reqParam, Map<String, Object> data) {
       
        if (CmmnUtil.isEmpty(data.get(DataKeys.tbInputDataXit.name())))
            throw new EnsException(EnsErrCd.NO_DATA_FOUND, EnsErrCd.NO_DATA_FOUND.getCodeNm());
    }

    @Override
    public void callApi(Map<String, Object> reqParam, Map<String, Object> data) {
        TbInputDataXit tbInputDataXit = (TbInputDataXit) data.get(DataKeys.tbInputDataXit.name());
        TbInputXit tbInputXit = tbInputDataXit.getTbInputXit();
        FsJob fsJob = tbInputXit.getFsJob();

        final String url = fsJob.getBpNvPayresultApi();


        Optional.ofNullable(gson.fromJson(gson.toJson(reqParam.get("body")), NvPayResultReqData.class))
                .map(nvPayResultReqData -> toEntity(nvPayResultReqData))
                .map(entity -> savePayResult(entity))
                .map(entity -> tbInputDataXit)
                .filter(detail -> "Y".equals(detail.getTbInputXit().getFsJob()))
                .map(vKakaoNoticeInfoSn -> payUseSysApi.payResult(url, reqParam, null))
                .map(respnose -> loadRespData(data, respnose))
                .map(response -> checkResponse(response))
        ;

    }

    private TbNaverPayResult toEntity(NvPayResultReqData nvPayResultReqData) {
        NvPayResultDetail detail = nvPayResultReqData.getDetail();
        final String billerUserKey = detail.getMerchantUserKey();

       
        final String jobSe = "EL".equals(billerUserKey.substring(0, 2)) ? "2" : "1";


        return TbNaverPayResult.builder()
                .dataId(billerUserKey)       
                .jobSe(jobSe)                                            
                .paymentId(detail.getPaymentId())                        
                .payHistId(detail.getPayHistId())                        
                .merchantId(detail.getMerchantId())                      
                .merchantName(detail.getMerchantName())                  
                .merchantPayKey(detail.getMerchantPayKey())              
                .merchantUserKey(detail.getMerchantUserKey())            
                .admissionTypeCode(detail.getAdmissionTypeCode())        
                .admissionYmdt(detail.getAdmissionYmdt())                
                .tradeConfirmYmdt(detail.getTradeConfirmYmdt())          
                .admissionState(detail.getAdmissionState())              
                .totalPayAmount(detail.getTotalPayAmount())              
                .primaryPayAmount(detail.getPrimaryPayAmount())          
                .npointPayAmount(detail.getNpointPayAmount())            
                .giftCardAmount(detail.getGiftCardAmount())              
                .taxScopeAmount(detail.getTaxScopeAmount())              
                .taxExScopeAmount(detail.getTaxExScopeAmount())          
                .environmentDepositAmount(detail.getEnvironmentDepositAmount())
                .primaryPayMeans(detail.getPrimaryPayMeans())            
                .cardCorpCode(detail.getCardCorpCode())                  
                .cardNo(detail.getCardNo())                              
                .cardAuthNo(detail.getCardAuthNo())                      
                .cardInstCount(detail.getCardInstCount())                
                .usedCardPoint(detail.getUsedCardPoint())                
                .bankCorpCode(detail.getBankCorpCode())                  
                .bankAccountNo(detail.getBankAccountNo())                
                .productName(detail.getProductName())                    
                .settleExpected(detail.getSettleExpected())              
                .settleExpectAmount(detail.getSettleExpectAmount())      
                .payCommissionAmount(detail.getPayCommissionAmount())    
                .extraDeduction(detail.getExtraDeduction())              
                .useCfmYmdt(detail.getUseCfmYmdt())                      
                .merchantExtraParameter(detail.getMerchantExtraParameter())
                .userIdentifier(detail.getUserIdentifier())              
                .payResultProcessSttus("0")                              
//                    .creatDt()                                         
                .crtr("ENS_SYS")                                         
//                    .updtDt()                                          
//                    .updusr()                                          
                .build();

    }

    private TbNaverPayResult savePayResult(TbNaverPayResult entity) {
        tbNaverPayResultRepository.save(entity);
        return entity;
    }

    private ResponseEntity<String> loadRespData(Map<String, Object> data, ResponseEntity<String> response) {
        data.put(DataKeys.responseJsonStr.name(), response.getBody());
        return response;
    }

    private PayApiRespDTO<Map<String, Object>> checkResponse(ResponseEntity<String> response) {
        
        if (!HttpStatus.OK.equals(response.getStatusCode()))
            throw new EnsException(EnsErrCd.API_COMM_ERROR, EnsErrCd.API_COMM_ERROR.getCodeNm() + " " + response.getStatusCode().toString(), response.getBody());
        
        PayApiRespDTO<Map<String, Object>> result = null;
        try {
            result = gson.fromJson(response.getBody(), PayApiRespDTO.class);
        } catch (Exception ex) {
            throw new EnsException(EnsErrCd.INVALID_RESPONSE, EnsErrCd.INVALID_RESPONSE.getCodeNm(), ex);
        }

        return result;
    }


    @Override
    public PayApiRespDTO<Map<String, Object>> generateResult(Map<String, Object> reqParam, Map<String, Object> data, EnsException e) {
        if (e == null) {    
            return PayApiRespDTO.<Map<String, Object>>okBuilder().build();

        } else {   
            return PayApiRespDTO.<Map<String, Object>>errDataBuilder()
                    .resCode(PayUseSysResCd.FAILED_SAVE_PAYRSLT.getCode())
                    .message(PayUseSysResCd.FAILED_SAVE_PAYRSLT.getCodeNm())
                    .data(null)
                    .build();
        }
    }

    @Override
    public void saveHist(Map<String, Object> reqParam, Map<String, Object> data, EnsException e) {
        Map<String, Object> body = (Map<String, Object>) reqParam.getOrDefault("body", new HashMap<>());
        Map<String, Object> detail = (Map<String, Object>) body.getOrDefault("detail", new HashMap<>());
        String billerUserKey = (String) detail.get("merchantUserKey");

        String errorCode = null;
        String errorMessage = null;
        if (e != null) {
            errorCode = e.getErrCd().getCode();
            errorMessage = String.format("%s %s", e.getMessage(), CmmnUtil.printStackTraceToString(e));
        }

        TbIupBillPayHist tbIupBillPayHist = TbIupBillPayHist.builder()
                .dataId(billerUserKey)
                .paidType(PaidTypeCd.naverPay)   
                .hisSeCd(PayUseSysHisSeCd.payResult)   
                .requestData(gson.toJson(reqParam))
                .responseData((String) data.get(DataKeys.responseJsonStr.name()))
                .error(FieldError.initBuilder()
                        .errorCode(errorCode)
                        .errorMessage(errorMessage)
                        .build())
                .build();
        tbIupBillPayHistRepository.save(tbIupBillPayHist);
    }
}
