package com.zbkj.service.wangshang.handle;

import com.zbkj.service.wangshang.api.MybankApiException;
import com.zbkj.service.wangshang.api.MybankApiExceptionEnum;
import com.zbkj.service.wangshang.api.MybankConstants;
import com.zbkj.service.wangshang.api.config.FunctionEnum;
import com.zbkj.service.wangshang.api.config.MybankConfig;
import com.zbkj.service.wangshang.api.domain.*;
import com.zbkj.service.wangshang.api.internal.mapping.DefaultSigner;
import com.zbkj.service.wangshang.api.internal.util.JaxbUtil;
import com.zbkj.service.wangshang.api.internal.util.MybankSignature;
import com.zbkj.service.wangshang.api.service.notify.BkcloudfundsNotifyResponseBody;
import com.zbkj.service.wangshang.api.service.notify.BkmerchantSettleNotifyPayResultResponseBody;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringWriter;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.TreeMap;

/**
 * 统一处理
 **/
@Component
public class CommomRequstHandle {

    @Autowired
    private StrategyFactory strategyFactory;


    public String getXmlContextString(HttpServletRequest httpServletRequest) throws MybankApiException {
        StringWriter writer = new StringWriter();
        BufferedReader reader = null;
        try {
            reader = httpServletRequest.getReader();
            char[] chars = new char[256];
            int count = 0;
            while ((count = reader.read(chars)) > 0) {
                writer.write(chars, 0, count);
            }
        } catch (Exception e) {
            //TODO 可以抛出运行时异常
            throw new MybankApiException(MybankApiExceptionEnum.SERVER_SYSTEM_EXCEPTION, e);
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    throw new MybankApiException(MybankApiExceptionEnum.SERVER_SYSTEM_EXCEPTION, e);
                }
            }
        }
        return writer.toString();
    }

    public String notifyRequstHandle(String xmlContext) throws MybankApiException {
        String functionName = null;
        String result = null;
        try {
            functionName = getNotifyFunction(xmlContext);
            result = strategyFactory.getStrategy(FunctionEnum.getStrategyValue(functionName)).notifyHandle(xmlContext, functionName);
        } catch (Exception e) {
            throw new MybankApiException(e);
        }
        return result;
    }

    /**
     * @param xmlContext 请求的xml报文
     * @Description 对通知报文进行校验
     **/
    public void checkNotifyRequest(String xmlContext) throws MybankApiException {
        //验签操作
        boolean signBool = checkSign(xmlContext);
        if (!signBool) {
            throw new MybankApiException(MybankApiExceptionEnum.VERIFY_FAIL);
        }
        //通过验证appId,确定通知是发送到本isv的。
        String notifyAppId = getNotifyAppId(xmlContext);
        if (!MybankConfig.appId.equals(notifyAppId)) {
            throw new MybankApiException(MybankApiExceptionEnum.NOTICE_NOT_MATCH);
        }
    }

    /**
     * @param xmlContext 请求的xml报文
     * @return 验签结果
     * @Description 对通知报文验签
     **/
    public boolean checkSign(String xmlContext) throws MybankApiException {
        //验签
        boolean check = false;
        try {
            check = MybankSignature.check(xmlContext, MybankConfig.mybankPublicKey, MybankConfig.charset, MybankConfig.signType);
        } catch (MybankApiException e) {
            throw new MybankApiException(e);
        }
        return check;
    }

    /**
     * @param xmlContext 请求的xml报文
     * @return TreeMap 请求头信息
     * @Description 获取请求头的信息
     **/
    public TreeMap<String, String> getNotifyHead(String xmlContext) throws MybankApiException {
        TreeMap<String, String> headMap = new TreeMap();
        Document doc = MybankSignature.parseDocumentByString(xmlContext);
        Element root = doc.getDocumentElement();
        Node head = root.getElementsByTagName(MybankConstants.HEAD).item(0);
        NodeList childNodes = head.getChildNodes();
        for (int i = 0; i < childNodes.getLength(); i++) {
            if (childNodes.item(i).getNodeType() == Node.ELEMENT_NODE) {
                headMap.put(childNodes.item(i).getNodeName(), childNodes.item(i).getTextContent());
            }
        }
        return headMap;
    }

    /**
     * @param xmlContext 请求的xml报文
     * @return appId
     * @Description 获取请求头中的appId信息
     **/
    public String getNotifyAppId(String xmlContext) throws MybankApiException {
        TreeMap<String, String> notifyHead = getNotifyHead(xmlContext);
        String notifyAppId = null;
        notifyAppId = notifyHead.get(MybankConstants.APPID);
        if(null == notifyAppId){
            notifyAppId = notifyHead.get(MybankConstants.APP_ID);
        }
        return notifyAppId;
    }

    /**
     * @param xmlContext 请求的xml报文
     * @return function 通知的接口名
     * @Description 获取请求头中的function信息
     **/
    public String getNotifyFunction(String xmlContext) throws MybankApiException {
        TreeMap<String, String> notifyHead = getNotifyHead(xmlContext);
        return notifyHead.get(MybankConstants.FUNCTION);
    }

    /**
     * @param flag 持久化处理结果
     * @param head 请求头信息
     * @return 加签后的响应串
     * @Description 统一处理响应报文加签
     **/
    public String getSignResult(boolean flag, RequestHead head) throws MybankApiException {
        RespInfo respInfo = new RespInfo();
        if (flag) {
            respInfo.setResultCode("0000");
            respInfo.setResultMsg("成功");
            respInfo.setResultStatus("S");
        } else {
            respInfo.setResultCode("9000");
            respInfo.setResultMsg("失败");
            respInfo.setResultStatus("F");
        }
        BkcloudfundsNotifyResponseBody responseBody = new BkcloudfundsNotifyResponseBody();
        responseBody.setRespInfo(respInfo);
        return responseAndSign(head, responseBody);
    }

    /**
     * @param head         通知报文的RequestHead
     * @param responseBody 封装的响应body
     * @return 加签后的响应串
     * @Description 统一处理响应报文加签
     **/
    public String responseAndSign(RequestHead head, ResponseBody responseBody) throws MybankApiException {
        ResponseHead responseHead;
        if (null != head.getAppid()) {
            responseHead = ResponseHead.builder(head.getVersion(), head.getAppId(),  null, head.getFunction(), head.getReqMsgId())
                    .inputCharset(MybankConstants.CHARSET_UTF8)
                    .respTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern(MybankConstants.DATE_TIME_FORMAT)))
                    .respTimeZone(MybankConstants.DATE_TIMEZONE)
                    .reserve(MybankConstants.NULL_STRING)
                    .signType(MybankConstants.SIGN_TYPE_RSA)
                    .build();
        } else {
            responseHead = ResponseHead.builder(head.getVersion(), head.getAppId(),  null, head.getFunction(), head.getReqMsgId())
                    .inputCharset(MybankConstants.CHARSET_UTF8)
                    .respTime(LocalDateTime.now().format(DateTimeFormatter.ofPattern(MybankConstants.DATE_TIME_FORMAT)))
                    .respTimeZone(MybankConstants.DATE_TIMEZONE)
                    .reserve(MybankConstants.NULL_STRING)
                    .signType(MybankConstants.SIGN_TYPE_RSA)
                    .build();
        }

        ResponseDocument responseDocument = new ResponseDocument(new Response(responseHead, responseBody));
        DefaultSigner signer = new DefaultSigner(MybankConfig.privateKey);
        String result = null;
        String xml = null;
        try {
            xml = JaxbUtil.convertToXml(responseDocument, MybankConstants.CHARSET_UTF8);
            result = signer.notifyResponseSign(xml, MybankConfig.charset, MybankConfig.signType);
        } catch (MybankApiException e) {
            throw new MybankApiException(e);
        }
        return result;
    }

    /**
     * @param flag 持久化处理结果
     * @param head 请求头信息
     * @return 打款结果通知加签后的响应串
     * @Description 打款结果通知响应报文加签
     **/
    public String getNotifyPayResult(boolean flag, RequestHead head) throws MybankApiException {
        BkmerchantSettleNotifyPayResultResponseBody notifyPayResultResponseBody = new BkmerchantSettleNotifyPayResultResponseBody();
        notifyPayResultResponseBody.setIsvOrgId(MybankConfig.isvOrgId);
        if (flag) {
            notifyPayResultResponseBody.setResponseCode(MybankConstants.OK);
        } else {
            notifyPayResultResponseBody.setResponseCode(MybankConstants.FAIL);
        }
        return responseAndSign(head, notifyPayResultResponseBody);
    }
}
