package com.dxhy.jxpt.baseService.sign.service.openservice.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.dxhy.api.model.sign.openservice.*;
import com.dxhy.common.utils.MakeAppSecService;
import com.dxhy.jxpt.baseService.sign.service.openservice.IOpenServicePlatformService;
import com.dxhy.jxpt.baseService.util.FpzlChangeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

/**
 * @author user
 */

@Slf4j
@Service
@SuppressWarnings("AlibabaUndefineMagicConstant")

public class OpenServicePlatformServiceImpl implements IOpenServicePlatformService {

    @Value("${openService.token.appKey:}")
    private String openServiceAppKey;

    @Value("${openService.token.appSecret:}")
    private String openServiceAppSecret;

    @Value("${openService.token.entCode:}")
    private String entCode;

    @Value("${openService.check.url:}")
    private String openServiceCheckUrl;

    @Value("${openService.ocr.url:}")
    private String openServiceOcrUrl;

    @Value("${openService.token.url:}")
    private String openServiceUrl;

    @Value("${openService.username:}")
    private String ocrUserName;
    @Value("${openService.password:}")
    private String ocrPassword;
    @Value("${openService.taxno:}")
    private String ocrTaxno;
    @Value("${openService.ocrUrl:}")
    private String ocrUrl;
    @Value("${openService.ocrEnterprisePermissionFlag}")
    private boolean ocrEnterprisePermissionFlag;
    @Resource
    private MakeAppSecService makeAppSecService;

    /**
     * 开放平台通用请求接口
     *
     * @param requestUrl
     *            请求连接
     * @param content
     *            参数密文
     * @param uuid
     *            请求流水号（32位随机数）
     * @param identify
     *            请求标识（什么请求，干啥的）
     * @param resultClass
     *            返回结果类
     * @return <T> 返回结果
     */
    private <T> T openServiceRequest(String requestUrl, String content, String uuid, String identify,
        Class<T> resultClass) throws OpenServiceInvokeException {
        try {
            // 生成token请求参数
            OpenServiceTokenRequest tokenRequest = new OpenServiceTokenRequest(openServiceAppKey, openServiceAppSecret);
            log.info("开始请求开放平台token，请求参数：{}", JSON.toJSONString(tokenRequest));
            // token请求结果
            String tokenResult = HttpUtil.post(openServiceUrl, JSON.toJSONString(tokenRequest), 30000);
            log.info("开放平台token请求完成，结果：{}", tokenResult);
            // 获取token值
            String accessToken = JSON.parseObject(tokenResult).getString("access_token");
            if (StringUtils.isEmpty(accessToken)) {
                log.info("调用开放平台token失败");
                throw new OpenServiceInvokeException("开放平台系统异常");
            }
            // 请求参数
            OpenServiceRequest request = new OpenServiceRequest(uuid, identify, content);
            log.info("开始请求开放平台接口，参数：" + JSON.toJSONString(request));
            // 请求结果
            String rs = HttpUtil.post(requestUrl + accessToken, JSON.toJSONString(request), 30000);
            log.info("请求开放平台接口返回：{}", rs);
            OpenServiceResponse openServiceResponse = JSON.parseObject(rs, new TypeReference<OpenServiceResponse>() {});
            String result = Optional.ofNullable(openServiceResponse).map(OpenServiceResponse::getReturnStateInfo)
                .map(OpenServiceReturnStateInfo::getReturnCode).orElse(null);
            if ("0000".equals(result)) {
                String checkResult = Base64.decodeStr(openServiceResponse.getContent());
                log.info("解析content内容为:{}",checkResult);
                return JSON.parseObject(checkResult).toJavaObject(resultClass);
            } else {
                log.info("开放平台接口调用返回失败：{}", JSON.toJSONString(openServiceResponse));
                throw new OpenServiceInvokeException("调用开放平台接口失败");
            }
        } catch (Exception e) {
            log.error("开放平台接口调用代码异常", e);
            throw new OpenServiceInvokeException("开放平台接口调用失败");
        }
    }

    /**
     * 开放平台单一发票查验接口
     *
     * @param request
     *            请求参数
     * @return 返回参数
     */
    @Override
    public OpenServiceSingleInvoiceCheckJxpt checkSingleInvoice(OpenServiceSingleInvoiceCheckRequest request)
        throws OpenServiceInvokeException {
        //统一处理请求参数
        // 日期，去掉-
        if(StringUtils.isNotBlank(request.getKprq())){
            request.setKprq(request.getKprq().replace("-", ""));
        }
        //校验码，如果大于6位，取后6位
        if(StringUtils.isNotBlank(request.getJym())){
            if(request.getJym().length()>6){
                request.setJym(request.getJym().substring(request.getJym().length()-6, request.getJym().length()));
            }
        }

        // 查验参数
        String content = Base64.encode(JSON.toJSONString(request));
        OpenServiceSingleInvoiceCheckResponse response =
            openServiceRequest(openServiceCheckUrl, content, UUID.randomUUID().toString().replace("-", ""),
                    entCode, OpenServiceSingleInvoiceCheckResponse.class);
        if (response == null || response.getData() == null) {
            log.info("查验接口调用返回成功，但解析返回值失败，content：{}", JSON.toJSONString(response));
            throw new OpenServiceInvokeException("调用查验接口失败");
        }
        OpenServiceSingleInvoiceCheck checkData = response.getData();
        OpenServiceSingleInvoiceCheckJxpt checkDataJxpt = new OpenServiceSingleInvoiceCheckJxpt();
        BeanUtils.copyProperties(checkData, checkDataJxpt);
        //如果查验成功，转化票种为进项
        if("0001".equals(checkData.getCyjg())){
            //如果是21 22，61,83赋值数电号码字段
            if("21".equals(checkData.getFpzl())||"22".equals(checkData.getFpzl())
                ||"61".equals(checkData.getFpzl())||"83".equals(checkData.getFpzl())
            ){
                checkDataJxpt.setEleInvoiceNo(checkData.getFphm());
                checkDataJxpt.setFpdm(checkData.getFphm().substring(0, 12));
                checkDataJxpt.setFphm(checkData.getFphm().substring(12, 20));
            }
            //数电纸质（普票/专票）。且jym不为空，则赋值数电号码字段
            if("01".equals(checkData.getFpzl())&&"1".equals(checkData.getXdpbz())){
                if(StringUtils.isNotBlank(checkData.getJym())){
                    checkDataJxpt.setEleInvoiceNo(checkData.getJym());
                }
            }
            if("04".equals(checkData.getFpzl())&&"1".equals(checkData.getXdpbz())){
                if(StringUtils.isNotBlank(checkData.getJym())){
                    checkDataJxpt.setEleInvoiceNo(checkData.getJym());
                }
            }
            //改变票种为进项系统票种
            checkDataJxpt.setFpzl(FpzlChangeUtil.getInvoiceTypeKfptToJxpt(checkData.getFpzl(), checkData.getXdpbz()));
            return checkDataJxpt;
        }else{
            return checkDataJxpt;
        }
    }

    @Override
    public List<OpenServiceOcr> getCustomsOcrResult(String uuid, String picture) throws OpenServiceInvokeException {
        OpenServiceOcrRequest ocrRequest = new OpenServiceOcrRequest(uuid, picture);
        String content = Base64.encode(JSON.toJSONString(ocrRequest));
        OpenServiceOcrResponse response =
            openServiceRequest(openServiceOcrUrl, content, uuid, entCode, OpenServiceOcrResponse.class);
        String result = Optional.ofNullable(response).map(OpenServiceOcrResponse::getReturnStateInfo)
            .map(OpenServiceReturnStateInfo::getReturnCode).orElse(null);
        if (!"0000".equals(result)) {
            log.info("OCR识别接口调用返回成功，但解析返回值失败，content：{}", JSON.toJSONString(response));
            throw new OpenServiceInvokeException("调用OCR识别接口失败");
        }
        return response.getInvoice();
    }

    @Override
    public List<OpenServiceOcr> ocrInvoice(String picture, String userName, String password, String taxno) {

        String sendDate = DateUtil.format(new Date(), DatePattern.PURE_DATETIME_PATTERN);
        String signStr = "username=%s&sendDate=%s&version=%s";
        String version = "1.0";
        log.info("传入的username:{},password:{},taxno:{}",userName,password,taxno);
        if (ocrEnterprisePermissionFlag || StringUtils.isBlank(userName) || StringUtils.isBlank(password) || StringUtils.isBlank(taxno)) {
            userName = ocrUserName;
            password = ocrPassword;
            taxno = ocrTaxno;
        }
        log.info("处理后的username:{},password:{},taxno:{}",userName,password,taxno);
        signStr = String.format(signStr, userName, sendDate, version);
        JSONObject dataJson = new JSONObject();
        dataJson.put("taxno", taxno);
        dataJson.put("picture", picture);
        JSONObject json = new JSONObject();
        json.put("username", userName);
        json.put("password", password);
        json.put("sendDate", sendDate);
        json.put("signature", makeAppSecService.getSign(signStr, password));
        json.put("version", version);
        log.info("请求报文，去除picture：{} ， picture是否有值：{}", json.toJSONString(), StringUtils.isNotBlank(picture));
        json.put("data", Base64.encode(dataJson.toString()));

        log.info("ocr识别请求地址:{}",ocrUrl);
        String post = HttpUtil.post(ocrUrl, json.toString(), 60000);
        log.info("ocr识别结果：{}", post);
        JSONObject jsonObject = JSONObject.parseObject(post, JSONObject.class);
        String returnCode = jsonObject.getString("returnCode");

        if (!"0000".equals(returnCode)) {
            return null;
        }

        String decodeStr = Base64.decodeStr(jsonObject.getString("data"));
        return JSONObject.parseArray(decodeStr, OpenServiceOcr.class);
    }
}
