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.TbKakaoPayResultRepository;
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.kko.service.process.model.KkoPayResultReqData;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.api.PayUseSysApi;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.code.PayUseSysHisSeCd;
import cokr.xit.ens.modules.common.ctgy.intgrnbill.support.code.PaidTypeCd;
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 IupKkoPayResultService implements PayResultService<Map<String, Object>, Map<String, Object>, PayApiRespDTO<Map<String, Object>>> {

    private final JPAQueryFactory query;
    private final PayUseSysApi payUseSysApi;
    private final TbKakaoPayResultRepository tbKakaoPayResultRepository;
    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> reqData = (Map<String, Object>) reqParam.getOrDefault("data", new HashMap<>());
        String billerUserKey = (String) reqData.get("biller_user_key");

        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.getBpKkoPayresultApi();    


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

    }

    private TbKakaoPayResult toEntity(KkoPayResultReqData kkoPayResultReqData) {

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


        final String payAmount = CmmnUtil.isEmpty(kkoPayResultReqData.getPayAmount()) ? null : String.valueOf(kkoPayResultReqData.getPayAmount());
        final String payFee = CmmnUtil.isEmpty(kkoPayResultReqData.getPayFee()) ? null : String.valueOf(kkoPayResultReqData.getPayFee());
        final String payFeeTax = CmmnUtil.isEmpty(kkoPayResultReqData.getPayFeeTax()) ? null : String.valueOf(kkoPayResultReqData.getPayFeeTax());
        final String payId = CmmnUtil.isEmpty(kkoPayResultReqData.getPayId()) ? null : String.valueOf(kkoPayResultReqData.getPayId());

        return TbKakaoPayResult.builder()
                .dataId(kkoPayResultReqData.getBillerUserKey())       
                .jobSe(jobSe)                                          
                .payMn(kkoPayResultReqData.getPayBy())                 
                .payTy(kkoPayResultReqData.getPayType())               
                .payDetail1(kkoPayResultReqData.getPayDetail1())       
                .payDetail2(kkoPayResultReqData.getPayDetail2())       
                .payDetail3(kkoPayResultReqData.getPayDetail3())       
                .payAmount(payAmount)         
                .payFeeTy(kkoPayResultReqData.getPayFeeType())        
                .payFeeAmount(payFee)         
                .payFeeVat(payFeeTax)        
                .excclcPrearngeDe(kkoPayResultReqData.getAdjustDate()) 
                .payDt(kkoPayResultReqData.getPaidAt())                
                .payId(payId)                 
                .payResultProcessSttus("0")                         
//                    .creatDt()                                    
                .crtr("ENS_SYS")                                    
//                    .updtDt()                                     
//                    .updusr()                                     
                .build();

    }

    private TbKakaoPayResult savePayResult(TbKakaoPayResult entity) {
        tbKakaoPayResultRepository.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> reqData = (Map<String, Object>) reqParam.getOrDefault("data", new HashMap<>());
        String billerUserKey = (String) reqData.get("biller_user_key");

        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.kakaoPay)   
                .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);
    }
}
