package cn.hanghui.outapi.platform.xinghuo.service.impl;

import cn.hanghui.outapi.platform.alipay.entity.enums.AlipayCertTypeEnum;
import cn.hanghui.outapi.platform.common.exception.BusinessException;
import cn.hanghui.outapi.platform.common.utils.FileUtils;
import cn.hanghui.outapi.platform.common.utils.ImageUtil;
import cn.hanghui.outapi.platform.common.utils.Preconditions;
import cn.hanghui.outapi.platform.common.utils.Sm4Utils;
import cn.hanghui.outapi.platform.common.utils.SnowflakeIdWorker;
import cn.hanghui.outapi.platform.tenant.entity.po.ProvisionalIdentityProofDevice;
import cn.hanghui.outapi.platform.tenant.entity.po.ProvisionalIdentityProofSpecialCode;
import cn.hanghui.outapi.platform.tenant.mapper.ProvisionalIdentityProofDeviceMapper;
import cn.hanghui.outapi.platform.tenant.mapper.ProvisionalIdentityProofSpecialCodeMapper;
import cn.hanghui.outapi.platform.xinghuo.entity.dto.SpecialCodeApplyReq;
import cn.hanghui.outapi.platform.xinghuo.entity.dto.SpecialCodeApplyResp;
import cn.hanghui.outapi.platform.xinghuo.entity.dto.SpecialCodeEncryptReq;
import cn.hanghui.outapi.platform.xinghuo.entity.dto.SpecialCodeEncryptResp;
import cn.hanghui.outapi.platform.xinghuo.entity.dto.SpecialCodeUserInfoReq;
import cn.hanghui.outapi.platform.xinghuo.entity.dto.SpecialCodeUserInfoResp;
import cn.hanghui.outapi.platform.xinghuo.service.ProofOpenApiService;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.img.ImgUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.extra.qrcode.QrCodeUtil;
import cn.hutool.extra.qrcode.QrConfig;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.util.Base64Utils;

import javax.annotation.Resource;
import java.io.ByteArrayInputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.Base64;
import java.util.Date;

/**
 * @author shisl
 * @package cn.hanghui.outapi.platform.xinghuo.service.impl
 * @class ProofOpenApiServiceImpl
 * @date 2024/7/30 下午2:59
 * @description
 */
@Slf4j
@Service
public class ProofOpenApiServiceImpl implements ProofOpenApiService {

    @Value("${proof.appId}")
    private String appId;

    @Value("${proof.domain}")
    private String domain;

    @Value("${proof.page}")
    private String page;

    @Value("${proof.schemePrefix}")
    private String schemePrefix;

    @Value("${proof.appKey}")
    private String appKey;

    @Value("${proof.appSecret}")
    private String appSecret;

    @Value("${proof.privateKey}")
    private String privateKey;

    @Value("${web.address}")
    private String address;

    @Resource
    private ProvisionalIdentityProofDeviceMapper provisionalIdentityProofDeviceMapper;

    @Resource
    private ProvisionalIdentityProofSpecialCodeMapper provisionalIdentityProofSpecialCodeMapper;


    @Override
    public SpecialCodeEncryptResp applySpecialCodeEncrypt(SpecialCodeEncryptReq req) {
        //校验参数
        checkEncryptData(req);
        //验签
        Boolean checkSign = Sm4Utils.checkSign(appKey, appSecret, req.getSign(), String.valueOf(req.getReqTimestamp()));
        if (!checkSign) {
            throw new BusinessException("验签失败");
        }
        //内容解密
        String decrypt = Sm4Utils.decrypt(privateKey, req.getBizContent());
        log.info("--->根据三要素获取二维码内容解密：{}", decrypt);
        if (Preconditions.isBlank(decrypt)) {
            throw new BusinessException("内容解密失败");
        }
        //数据转换
        SpecialCodeApplyReq decryptContent = JSONObject.parseObject(decrypt, SpecialCodeApplyReq.class);
        //校验解密内容
        checkDecryptContent(decryptContent);
        //处理业务数据并生成特殊二维码
        SpecialCodeApplyResp resp = handleBizContent(decryptContent);
        //生成签名
        long timestamps = System.currentTimeMillis();
        String sign = Sm4Utils.getSign(appKey, appSecret, String.valueOf(timestamps));
        //加密数据
        String encrypt = Sm4Utils.encrypt(privateKey, JSONObject.toJSONString(resp));
        SpecialCodeEncryptResp encryptResp = new SpecialCodeEncryptResp();
        encryptResp.setReqTimestamp(timestamps);
        encryptResp.setSign(sign);
        encryptResp.setBizContent(encrypt);
        return encryptResp;
    }

    @Override
    public SpecialCodeApplyResp applySpecialCode(SpecialCodeApplyReq req) {
        //校验内容
        checkDecryptContent(req);
        //处理业务数据并生成特殊二维码
        SpecialCodeApplyResp resp = handleBizContent(req);
        //加密数据
        return resp;
    }

    @Override
    public SpecialCodeUserInfoResp queryUserInfoByMiniSpecialCode(SpecialCodeUserInfoReq req) {
        DateTime dateTime = DateUtil.parse(DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN), DatePattern.NORM_DATE_PATTERN);
        LambdaQueryWrapper<ProvisionalIdentityProofSpecialCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProvisionalIdentityProofSpecialCode::getQrCodeNo, req.getQrCode())
                .le(ProvisionalIdentityProofSpecialCode::getStartDate, dateTime)
                .ge(ProvisionalIdentityProofSpecialCode::getEndDate, dateTime)
                .eq(ProvisionalIdentityProofSpecialCode::getIsDeleted, Boolean.FALSE);
        ProvisionalIdentityProofSpecialCode specialCode = provisionalIdentityProofSpecialCodeMapper.selectOne(wrapper);
        if (Preconditions.isBlank(specialCode) || Preconditions.isBlank(specialCode.getEndDate())) {
            throw new BusinessException("二维码不存在或已过期");
        }
        SpecialCodeUserInfoResp resp = BeanUtil.copyProperties(specialCode, SpecialCodeUserInfoResp.class);
        if (Preconditions.isNotBlank(resp.getPhoto())) {
            resp.setPhotoBase64(ImageUtil.getImageBase64FromUrl(resp.getPhoto()));
        }
        return resp;
    }

    @Override
    public SpecialCodeUserInfoResp queryUserInfoBySpecialCode(SpecialCodeUserInfoReq req) {
        //解析二维码
        return analysisQrCode(req.getQrCode());
    }

    @Override
    public SpecialCodeEncryptResp queryUserInfoBySpecialCodeEncrypt(SpecialCodeEncryptReq req) {
        //校验参数
        checkEncryptData(req);
        //验签
        Boolean checkSign = Sm4Utils.checkSign(appKey, appSecret, req.getSign(), String.valueOf(req.getReqTimestamp()));
        if (!checkSign) {
            throw new BusinessException("验签失败");
        }
        //内容解密
        String decrypt = Sm4Utils.decrypt(privateKey, req.getBizContent());
        log.info("--->根据二维码换取三要素加密版原始入参内容解密：{}", decrypt);
        if (Preconditions.isBlank(decrypt)) {
            throw new BusinessException("内容解密失败");
        }
        //数据转换
        SpecialCodeUserInfoReq decryptContent = JSONObject.parseObject(decrypt, SpecialCodeUserInfoReq.class);
        //解析二维码
        if (Preconditions.isBlank(decryptContent) || Preconditions.isBlank(decryptContent.getQrCode())) {
            throw new BusinessException("解密内容参数有误");
        }
        SpecialCodeUserInfoResp userInfoResp = analysisQrCode(decryptContent.getQrCode());
        //生成签名
        long timestamps = System.currentTimeMillis();
        String sign = Sm4Utils.getSign(appKey, appSecret, String.valueOf(timestamps));
        //加密数据
        String encrypt = Sm4Utils.encrypt(privateKey, JSONObject.toJSONString(userInfoResp));
        SpecialCodeEncryptResp encryptResp = new SpecialCodeEncryptResp();
        encryptResp.setReqTimestamp(timestamps);
        encryptResp.setSign(sign);
        encryptResp.setBizContent(encrypt);
        return encryptResp;
    }

    private SpecialCodeUserInfoResp analysisQrCode(String qrCode) {
        //https://ldb-permit.hz-hanghui.com:8089?scheme=alipays%3A%2F%2Fplatformapi%2Fstartapp%3FappId%3D2021004161639724%26page%3Dpages%2Findex%2Findex%26query%3D%E5%88%98%E7%90%B3%E7%90%B3
        String qr = null;
        try {
            //获取二维码码值
            String doubleDecode = URLDecoder.decode(URLDecoder.decode(qrCode, "UTF-8"), "UTF-8");
            log.info("--->二次URL解码结果:{}", doubleDecode);
            if (doubleDecode.lastIndexOf("q=") < 0) {
                throw new BusinessException("码值解析失败");
            }
            qr = doubleDecode.substring(doubleDecode.lastIndexOf("q=") + 2);
        } catch (Exception e) {
            log.error("--->二维码解析失败");
        }
        if (Preconditions.isBlank(qr)) {
            throw new BusinessException("二维码解析失败");
        }
        DateTime dateTime = DateUtil.parse(DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN), DatePattern.NORM_DATE_PATTERN);
        LambdaQueryWrapper<ProvisionalIdentityProofSpecialCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProvisionalIdentityProofSpecialCode::getQrCodeNo, qr)
                .le(ProvisionalIdentityProofSpecialCode::getStartDate, dateTime)
                .ge(ProvisionalIdentityProofSpecialCode::getEndDate, dateTime)
                .eq(ProvisionalIdentityProofSpecialCode::getIsDeleted, Boolean.FALSE);
        ProvisionalIdentityProofSpecialCode specialCode = provisionalIdentityProofSpecialCodeMapper.selectOne(wrapper);
        if (Preconditions.isBlank(specialCode) || Preconditions.isBlank(specialCode.getEndDate())) {
            throw new BusinessException("二维码不存在或已过期");
        }
        SpecialCodeUserInfoResp resp = BeanUtil.copyProperties(specialCode, SpecialCodeUserInfoResp.class);
        if (Preconditions.isNotBlank(resp.getPhoto())) {
            resp.setPhotoBase64(ImageUtil.getImageBase64FromUrl(resp.getPhoto()));
        }
        return resp;
    }

    public static void main(String[] args) {
        try {
            String doubleDecode = URLDecoder.decode(URLDecoder.decode("qqq", "UTF-8"), "UTF-8");
            log.info(doubleDecode.substring(doubleDecode.lastIndexOf("q=") + 2));
        } catch (Exception e) {

        }
    }

    private SpecialCodeApplyResp handleBizContent(SpecialCodeApplyReq decryptContent) {
        SpecialCodeApplyResp resp = new SpecialCodeApplyResp();
        LambdaQueryWrapper<ProvisionalIdentityProofDevice> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProvisionalIdentityProofDevice::getSn, decryptContent.getSn()).eq(ProvisionalIdentityProofDevice::getIsDeleted, Boolean.FALSE);
        ProvisionalIdentityProofDevice device = provisionalIdentityProofDeviceMapper.selectOne(wrapper);
        if (Preconditions.isBlank(device)) {
            throw new BusinessException("设备未录入系统");
        }
        Long purchaserId = device.getPurchaserId();
        //TODO。。。校验数据是否存在 用户数据入库
        String qrCodeNo = DigestUtil.md5Hex(AlipayCertTypeEnum.IDENTITY_CARD.getCode() + decryptContent.getCertNo() + purchaserId + System.currentTimeMillis());
        ProvisionalIdentityProofSpecialCode specialCode = addProvisionalIdentityProofSpecialCode(decryptContent, purchaserId, qrCodeNo);
        //生成二维码
        String zfbSpecialCode = createSpecialCode(specialCode);
        //生成二维码图片
        byte[] bytes = QrCodeUtil.generatePng(zfbSpecialCode, 400, 400);
        String base64 = Base64Utils.encodeToString(bytes);
        BeanUtil.copyProperties(specialCode, resp);
        resp.setSpecialCode("data:image/jpeg;base64," + base64);
        resp.setQrCode(zfbSpecialCode);
        return resp;
    }

    private String createSpecialCode(ProvisionalIdentityProofSpecialCode specialCode) {
        //https://ldb-permit.hz-hanghui.com:8089?scheme=alipays%3A%2F%2Fplatformapi%2Fstartapp%3FappId%3D2021004161639724%26page%3Dpages%2Findex%2Findex%26query%3D%E5%88%98%E7%90%B3%E7%90%B3
        //alipays://platformapi/startapp?appId=2021004161639724&page=pages/index/index&query=qr%3DDwewew
        try {
            StringBuffer buffer = new StringBuffer(schemePrefix).append("?")
                    .append("appId").append("=").append(appId).append("&")
                    .append("page").append("=").append(page).append("&")
                    .append("query").append("=").append(URLEncoder.encode(("q=" + specialCode.getQrCodeNo()), "UTF-8"));
            return domain + "?scheme=" + URLEncoder.encode(buffer.toString(), "UTF-8");
        } catch (Exception e) {
            log.error("特殊二维码生成失败");
        }
        throw new BusinessException("特殊二维码生成失败");
    }

    private ProvisionalIdentityProofSpecialCode addProvisionalIdentityProofSpecialCode(SpecialCodeApplyReq decryptContent, Long purchaserId, String qrCodeNo) {
        DateTime dateTime = DateUtil.parse(DateUtil.format(new Date(), DatePattern.NORM_DATE_PATTERN), DatePattern.NORM_DATE_PATTERN);
        LambdaQueryWrapper<ProvisionalIdentityProofSpecialCode> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(ProvisionalIdentityProofSpecialCode::getPurchaserId, purchaserId)
                .eq(ProvisionalIdentityProofSpecialCode::getCertNo, decryptContent.getCertNo())
                .le(ProvisionalIdentityProofSpecialCode::getStartDate, dateTime)
                .ge(ProvisionalIdentityProofSpecialCode::getEndDate, dateTime)
                .eq(ProvisionalIdentityProofSpecialCode::getIsDeleted, Boolean.FALSE);
        ProvisionalIdentityProofSpecialCode specialCode = provisionalIdentityProofSpecialCodeMapper.selectOne(wrapper);
        if (Preconditions.isNotBlank(specialCode)) {
            log.error("用户【{}】二维码已申请", decryptContent.getCertNo());
            return modifySpecialCodeData(decryptContent, specialCode, qrCodeNo);
        }
        return insertSpecialCodeData(decryptContent, purchaserId, qrCodeNo, dateTime);
    }

    private ProvisionalIdentityProofSpecialCode insertSpecialCodeData(SpecialCodeApplyReq decryptContent, Long purchaserId, String qrCodeNo, DateTime dateTime) {
        ProvisionalIdentityProofSpecialCode specialCode = BeanUtil.copyProperties(decryptContent, ProvisionalIdentityProofSpecialCode.class);
        specialCode.setPurchaserId(purchaserId);
        specialCode.setQrCodeNo(qrCodeNo);
        specialCode.setCreateTime(new Date());
        specialCode.setUpdateTime(new Date());
        if (Preconditions.isBlank(decryptContent.getStartDate())
                || Preconditions.isBlank(decryptContent.getEndDate())) {
            specialCode.setStartDate(dateTime);
            specialCode.setEndDate(DateUtils.addDays(dateTime, 1));
        }
        specialCode.setIsDeleted(Boolean.FALSE);
        String photo = decryptContent.getPhotoBase64();
        if (Preconditions.isNotBlank(decryptContent.getPhotoBase64())) {
            try {
                String filePath = FileUtils.pic_photo_path;
                String filename = SnowflakeIdWorker.getSnowId() + ".jpg";
                String path = FileUtils.saveByCopy(filename, filePath, new ByteArrayInputStream(Base64.getDecoder().decode(decryptContent.getPhotoBase64())));
                photo = address.concat(path);
            } catch (Exception e) {
                log.error("照片转换失败");
            }
        }
        specialCode.setPhoto(photo);
        provisionalIdentityProofSpecialCodeMapper.insert(specialCode);
        return specialCode;
    }

    private ProvisionalIdentityProofSpecialCode modifySpecialCodeData(SpecialCodeApplyReq decryptContent, ProvisionalIdentityProofSpecialCode specialCode, String qrCodeNo) {
        specialCode.setUpdateTime(new Date());
        specialCode.setQrCodeNo(qrCodeNo);
        specialCode.setStartDate(decryptContent.getStartDate());
        specialCode.setEndDate(decryptContent.getEndDate());
        specialCode.setIssuingTime(decryptContent.getIssuingTime());
        specialCode.setIssuingAuthority(decryptContent.getIssuingAuthority());
        String photo = decryptContent.getPhotoBase64();
        if (Preconditions.isNotBlank(decryptContent.getPhotoBase64())) {
            try {
                String filePath = FileUtils.pic_photo_path;
                String filename = SnowflakeIdWorker.getSnowId() + ".jpg";
                String path = FileUtils.saveByCopy(filename, filePath, new ByteArrayInputStream(Base64.getDecoder().decode(decryptContent.getPhotoBase64())));
                photo = address.concat(path);
            } catch (Exception e) {
                log.error("照片转换失败");
            }
        }
        specialCode.setPhoto(photo);
        provisionalIdentityProofSpecialCodeMapper.updateById(specialCode);
        return specialCode;
    }

    private void checkDecryptContent(SpecialCodeApplyReq decryptContent) {
        if (Preconditions.isBlank(decryptContent.getSn())
                || Preconditions.isBlank(decryptContent.getCertNo())
                || Preconditions.isBlank(decryptContent.getCertName())
                || Preconditions.isBlank(decryptContent.getStartDate())
                || Preconditions.isBlank(decryptContent.getEndDate())
                || Preconditions.isBlank(decryptContent.getIssuingTime())) {
            throw new BusinessException("参数错误，请检查后重新提交");
        }
    }

    private void checkEncryptData(SpecialCodeEncryptReq req) {
        if (Preconditions.isBlank(req.getSign())) {
            throw new BusinessException("签名错误");
        }
        if (Preconditions.isBlank(req.getBizContent()) || Preconditions.isBlank(req.getReqTimestamp())) {
            throw new BusinessException("参数有误");
        }
    }
}
