package com.dxhy.erp.service.impl;

import cn.hutool.core.util.ReflectUtil;
import com.alibaba.fastjson.JSONObject;
import com.dxhy.common.constant.CommonConstants;
import com.dxhy.common.constant.DbConstant;
import com.dxhy.common.datasource.annotation.DataSource;
import com.dxhy.common.datasource.config.DynamicContextHolder;
import com.dxhy.common.service.impl.MpBaseServiceImpl;
import com.dxhy.common.utils.R;
import com.dxhy.erp.dao.RequestRecordDao;
import com.dxhy.erp.entity.BaseRequestRecordVO;
import com.dxhy.erp.entity.RequestRecord;
import com.dxhy.erp.enums.RequestRecordEnum;
import com.dxhy.erp.service.RequestRecordService;
import com.dxhy.erp.service.SNPushCheckRecordService;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.LinkedHashMap;
import java.util.List;

@Slf4j
@Service
public class RequestRecordServiceImpl extends MpBaseServiceImpl<RequestRecordDao, RequestRecord> implements RequestRecordService {


    private final String dbname = "business";

    @Resource
    private RequestRecordDao requestRecordDao;

    @Resource
    private SNPushCheckRecordService snPushCheckRecordService;


    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public int saveSuccessfulRequestRecord(RequestRecord record, String responseDetail) {
        record.setFailedTimes(0);
        record.setStatus(0);
        record.setResponseDetail(responseDetail);
        log.info("接口调用记录正在保存，调用记录详情:{}", JSONObject.toJSONString(record));
        int insert = requestRecordDao.insert(record);
        return insert;
    }

    @Override
    @Transactional(propagation = Propagation.REQUIRES_NEW, rollbackFor = Exception.class)
    public int saveFailedRequestRecord(RequestRecord record, Integer isRecall, String responseDetail, String failedReason) throws UnsupportedEncodingException {
        // 尝试修复因编码格式问题导致HY000
        record.setRequestDetail(record.getRequestDetail().getBytes("UTF-8").toString());
        record.setResponseDetail(responseDetail.getBytes("UTF-8").toString());

        record.setFailedTimes(1);
        record.setStatus(1);
        record.setFailedReason(failedReason);
        record.setIsRecall(isRecall);
        log.info("接口调用记录正在保存，调用记录详情:{}", JSONObject.toJSONString(record));
        int insert = requestRecordDao.insert(record);
        return insert;
    }

    @Override
    @DataSource(name = "business_business_read")
    public JSONObject getRecordList(String portName, Date startTime, Date endTime,
                                    Integer status, String methodName, Integer pageNumber, Integer pageSize, Integer orderBy) {
        JSONObject json = new JSONObject();
        try {
            PageHelper.startPage(pageNumber, pageSize);
            List<BaseRequestRecordVO> recordList = requestRecordDao.selectRecordList(portName, startTime, endTime, status, methodName, orderBy);

            PageInfo<BaseRequestRecordVO> pageInfo = new PageInfo<>(recordList);
            json.put("datalist",recordList);
            json.put("total",pageInfo.getTotal());

            log.info("查询结果:{}", JSONObject.toJSONString(recordList));
            return json;
        } catch (Exception e) {
            log.error("请求出现异常,异常信息:{}",e);
            json.put("datalist",null);
            return json;
        }
    }

    @Override
    @DataSource(name = "business_business_read")
    public JSONObject getDetailRecord(Long id) {
        log.info("查询的id:{}",id);
        JSONObject json = new JSONObject();
        try {
            RequestRecord detailRecord = requestRecordDao.selectById(id);
            log.info("查询结果:{}", JSONObject.toJSONString(detailRecord));
            return json;
        } catch (Exception e) {
            log.error("查询记录详情出现异常,异常信息:{}",e);
            json.put("data",null);
            return json;
        }
    }

    @Override
    public RequestRecord getBaseRequestRecord(String requestDetail, String portName, String invoiceCode, String invoiceNo, String methodName) {
        return RequestRecord.builder()
                .requestDetail(requestDetail)
                .portName(portName)
                .createTime(new Date())
                .invoiceCode(invoiceCode)
                .invoiceNo(invoiceNo)
                .methodName(methodName)
                .build();
    }

    @Override
    public RequestRecord getBaseRequestRecord(String requestDetail, String portName, String methodName) {
        return RequestRecord.builder()
                .requestDetail(requestDetail)
                .portName(portName)
                .createTime(new Date())
                .methodName(methodName)
                .build();
    }

    @Override
    public RequestRecord getBaseRequestRecord(String requestDetail, String portName, String invoiceNo, String methodName) {
        return RequestRecord.builder()
                .requestDetail(requestDetail)
                .portName(portName)
                .createTime(new Date())
                .invoiceNo(invoiceNo)
                .methodName(methodName)
                .build();
    }


    @Override
    public ResponseEntity<?> reCall(RequestRecord requestRecord) {
        try {
            DynamicContextHolder.push(dbname + DbConstant.BUSINESS_READ);
            RequestRecord dbRecord = requestRecordDao.selectById(requestRecord.getId());
            log.info("数据库匹配信息:{}", JSONObject.toJSONString(dbRecord));
            if (dbRecord == null) {
                return ResponseEntity.ok(JSONObject.toJSONString(R.error("请求信息错误，请确认后再发送！")));
            }
            if (dbRecord.getActiveCall() == null) {
                return ResponseEntity.ok(JSONObject.toJSONString(R.error("暂只支持发票系统主动调用外部系统的补偿！")));
            }
            if (dbRecord.getStatus() == 0) {
                return ResponseEntity.ok(JSONObject.toJSONString(R.error("该请求已成功，无法继续重调！")));
            }
            if (dbRecord.getIsRecall() == 0) {
                return ResponseEntity.ok(JSONObject.toJSONString(R.error("该请求参数不符合标准，无法进行重调！")));
            }

            RequestRecordEnum[] RequestRecordEnums = RequestRecordEnum.values();
            log.info("获取到的枚举类型:{}", JSONObject.toJSONString(RequestRecordEnums));
            for (RequestRecordEnum recordenum : RequestRecordEnums) {
                if (recordenum.getMethodName().equals(dbRecord.getActiveCall())) {
                    Method method = ReflectUtil.getMethodByName(snPushCheckRecordService.getClass(), recordenum.getMethodName());
                    log.info("通过反射获取到的方法名：{}", JSONObject.toJSONString(method.getName()));

                    Object invokeResponse = null;
                    R data = new R(1000, "default success");
                    try {
                        DynamicContextHolder.push(dbname + DbConstant.BUSINESS_WRITE);
                        List<String> arglist = JSONObject.parseObject(dbRecord.getActiveCallRequestDetail(), List.class);
                        List<Object> args = new ArrayList<>();
                        arglist.forEach(strArg -> args.add(JSONObject.parseObject(strArg, recordenum.getArgClass()[arglist.indexOf(strArg)])));

                        invokeResponse = method.invoke(snPushCheckRecordService, args.toArray());

                        log.info("重调后得到的结果:{}", JSONObject.toJSONString(invokeResponse));

                        JSONObject rspJson = JSONObject.parseObject(invokeResponse.toString(), JSONObject.class);

                        data.put("data", invokeResponse);

                        if (rspJson.containsKey("SAPKEY")) {
                            if (!rspJson.containsKey("处理成功")) {
                                updateFailedRecallRecord(dbRecord, 1, JSONObject.toJSONString(rspJson), JSONObject.toJSONString(data), rspJson.getString("ZMESSAGE"), 0);
                                return ResponseEntity.ok(JSONObject.toJSONString(data));
                            }
                        }
                        if (rspJson.containsKey("code")) {
                            updateFailedRecallRecord(dbRecord, 1, JSONObject.toJSONString(rspJson), JSONObject.toJSONString(data), rspJson.getString("message"), 0);
                            return ResponseEntity.ok(JSONObject.toJSONString(data));
                        } else {
                            if (rspJson.containsKey("cyjg")) {
                                updateFailedRecallRecord(dbRecord, 1, JSONObject.toJSONString(rspJson), JSONObject.toJSONString(data), rspJson.getString("cyjgxx"), 0);
                                return ResponseEntity.ok(JSONObject.toJSONString(data));
                            }
                        }

                        updateSuccessfulRecallRecord(dbRecord, 0, JSONObject.toJSONString(rspJson), JSONObject.toJSONString(data));
                        return ResponseEntity.ok(JSONObject.toJSONString(data));
                    } catch (Exception e) {
                        log.error("重调出现异常，异常信息:{}", e);

                        try {
                            updateFailedRecallRecord(dbRecord, 1, JSONObject.toJSONString(invokeResponse),
                                    JSONObject.toJSONString(data), "重调用出现异常:" + JSONObject.toJSONString(e.getMessage()), 1);
                        } catch (Exception exception) {

                            log.error("接口重调用保存异常：{}", exception);
                        }
                        return ResponseEntity.ok(JSONObject.toJSONString(R.error(CommonConstants.MSG_ERR_DEFAULT)));
                    }

                }
            }
        } catch (Exception e) {

            log.info("请求处理异常，异常信息:{}", e);
            return ResponseEntity.ok(JSONObject.toJSONString(R.error("请求处理异常，请校正参数！")));
        }

        return ResponseEntity.ok(JSONObject.toJSONString(R.error("未找到相应信息！")));
    }

    private int updateSuccessfulRecallRecord(RequestRecord record, Integer status, String activeCallRecallResponse, String recallResponseDetail) {
        setResponse(record, activeCallRecallResponse, recallResponseDetail);
        record.setLastRetryTime(new Date());
        record.setRetryTimes(record.getRetryTimes() + 1);
        record.setStatus(status);
        record.setIsRecall(0);

        return requestRecordDao.updateById(record);
    }

    private int updateFailedRecallRecord(RequestRecord record, Integer status, String activeCallResponse, String recallResponseDetail, String failedReason, Integer isRecall) {
        setResponse(record, activeCallResponse, recallResponseDetail);
        record.setLastRetryTime(new Date());
        record.setRetryTimes(record.getRetryTimes() + 1);
        record.setStatus(status);
        record.setFailedReason(failedReason);
        record.setIsRecall(isRecall);

        return requestRecordDao.updateById(record);
    }

    private void setResponse(RequestRecord record, String activeCallRecallResponse, String recallResponseDetail) {
        LinkedHashMap<String, String> activeCallrecallMap = JSONObject.parseObject(record.getRecallResponseDetail(), LinkedHashMap.class);
        LinkedHashMap<String, String> reCallMap = JSONObject.parseObject(record.getRecallResponseDetail(), LinkedHashMap.class);
        if (record.getRetryTimes() == 0) {
            activeCallrecallMap = new LinkedHashMap<>();
            reCallMap = new LinkedHashMap<>();
            activeCallrecallMap.put("第1次主动调用重调返回结果", activeCallRecallResponse);
            reCallMap.put("第1次重调返回结果", recallResponseDetail);
            log.info("第1次重调返回结果:{}",JSONObject.toJSONString(reCallMap));
        } else {
            reCallMap.put("第" + (record.getRetryTimes() + 1) + "次重调结果", recallResponseDetail);
            activeCallrecallMap.put("第" + (record.getRetryTimes() + 1) + "次主动调用重调结果", activeCallRecallResponse);
        }
        record.setActiveCallRecallResponse(JSONObject.toJSONString(activeCallrecallMap));
        record.setRecallResponseDetail(JSONObject.toJSONString(reCallMap));
    }

}
