/**
 * cljpay.com
 * Copyright (C) 2015-2023 All Rights Reserved.
 */package com.jeequan.jeepay.pay.ctrl.yly;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jeequan.jeepay.core.constants.ApiCodeEnum;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApp;
import com.jeequan.jeepay.core.exception.BizException;
import com.jeequan.jeepay.core.model.ApiRes;
import com.jeequan.jeepay.core.model.device.YlyParams;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRQ;
import com.jeequan.jeepay.core.model.rqrs.payorder.UnifiedOrderRS;
import com.jeequan.jeepay.core.model.rqrs.payorder.payway.AutoBarOrderRQ;
import com.jeequan.jeepay.core.utils.AmountUtil;
import com.jeequan.jeepay.core.utils.Base64;
import com.jeequan.jeepay.core.utils.JeepayKit;
import com.jeequan.jeepay.core.utils.SeqKit;
import com.jeequan.jeepay.db.entity.*;
import com.jeequan.jeepay.model.DeviceInfo;
import com.jeequan.jeepay.pay.ctrl.payorder.AbstractPayOrderController;
import com.jeequan.jeepay.pay.ctrl.yly.sdk.RequestMethod;
import com.jeequan.jeepay.service.impl.DeviceProvideConfigService;
import com.jeequan.jeepay.service.impl.MchStoreDeviceService;
import com.jeequan.jeepay.service.impl.PayWayService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.crypto.Cipher;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.servlet.http.HttpServletRequest;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.security.KeyFactory;
import java.security.PublicKey;
import java.security.Signature;
import java.security.spec.X509EncodedKeySpec;

/**
 * @author dingzhiwei
 * @version
 */
@Slf4j
@RestController
@RequestMapping("/api/yly")
public class YlyController extends AbstractPayOrderController {

    @Autowired private DeviceProvideConfigService deviceProvideConfigService;
    @Autowired private MchStoreDeviceService mchStoreDeviceService;
    @Autowired private PayWayService payWayService;

    /**
     * 易联云K8通知
     *
     * @param request
     * @return
     */
    @RequestMapping("/notify")
    public JSONObject ylNotify(HttpServletRequest request) {
        JSONObject object = new JSONObject();
        object.put("message", "ok");
        log.info("易联云K8消息：{}", getReqParamJSON());
        JSONObject reqObj = getReqParamJSON();
        if(reqObj == null || reqObj.isEmpty()) {
            return object;
        }
        String ciphertext = reqObj.getString("ciphertext");
        String nonce = reqObj.getString("nonce");
        String tag = reqObj.getString("tag");
        String iv = reqObj.getString("iv");
        String additional_data = reqObj.getString("additional_data");
        String signature = reqObj.getString("signature");
        // 解密key
        String key = "tfa62db9e93353f12ad94988dc9322em";
        try {
            String plaintext = decrypte(Base64.decodeBase64String(ciphertext), key.getBytes(), iv.getBytes(), Base64.decodeBase64String(tag));
            log.info("报文：{}", plaintext);
        } catch (Exception e) {
            log.error("解密异常,{}", e);
        }
        return object;
    }

    @RequestMapping("/oauth2")
    public JSONObject oauth2(HttpServletRequest request) {
        JSONObject object = new JSONObject();
        object.put("message", "ok");
        log.info("易联云K8消息：{}", getReqParamJSON());

        return object;
    }

    // TODO 验证签名暂未实现

    /**
     * AES-GCM-256对称解密
     * @param encryptedBytes
     * @param keyBytes
     * @param ivBytes
     * @param tagBytes
     * @return
     * @throws Exception
     */
    private String decrypte(byte[] encryptedBytes, byte[] keyBytes, byte[] ivBytes, byte[] tagBytes) throws Exception {
        SecretKeySpec secretKey = new SecretKeySpec(keyBytes, "AES");
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec gcmParameterSpec = new GCMParameterSpec(128, ivBytes);
        cipher.init(Cipher.DECRYPT_MODE, secretKey, gcmParameterSpec);
        cipher.updateAAD("transaction".getBytes(StandardCharsets.UTF_8));
        cipher.update(encryptedBytes);
        byte[] decryptedBytes = cipher.doFinal(tagBytes);
        String decryptedString = new String(decryptedBytes, StandardCharsets.UTF_8);
        byte[] content = Base64.decodeBase64String(decryptedString);
        return new String(content);
    }

    // 以下代码为临时测试
    public static void main(String[] args) throws Exception  {

        String clientId = "1071822737";
        String clientSecret = "9fa62db9e93353f09ad94988dc9322ee";

        String machine_code = "4004849730";
        String secret = "623550139267";

        String result;

        RequestMethod.getInstance().init(clientId, clientSecret);

        //result = RequestMethod.getInstance().getCodeOpen(URLEncoder.encode("https://jeepay.natapp4.cc/api/yly/oauth2"));
        //System.out.println("getCodeOpen = " + result);

        //result = RequestMethod.getInstance().scanCodeModel(machine_code, secret);
        //System.out.println("scancodemodel = " + result);
//        String token = RequestMethod.getInstance().getAccessToken();

        String accessToken = "40037cf4c0344b3f990a77eb9c116030"; // 自有 30天失效，到2023-10-19
//        String accessToken = "ca236d5254464d60b5b9832c3ef3fd92"; // 30天失效，到2023-10-19

        // 状态获取
        result = RequestMethod.getInstance().printerGetPrintStatus(accessToken, machine_code);
        System.out.println("/printer/getprintstatus = " + result);

        // 设置打印Logo
        //result = RequestMethod.getInstance().printSetIcon(accessToken, machine_code, "http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_logo.png");
        //System.out.println("/printer/printSetIcon = " + result);

        // 指令
        String content = "";
        // 支付下发
//        content = "<JSON>{\"order_payment\":{\"pay_amount\":\"0.03\",\"pay_amount_source\":3},\"processed_state\":0}</JSON><VI>040</VI><VI>043</VI><VD>0.03</VD><VI>032</VI>";

        // 支付成功
//        content = "<VI>033</VI><VI>041</VI><VI>031</VI><VD>0.03</VD><VI>032</VI>";  // 微信成功收款0.03元
        content = "<VI>033</VI><VA>0,100</VA>";     // 微信收款100元
        // 文本打印
        result = RequestMethod.getInstance().printIndex(accessToken, machine_code, URLEncoder.encode(content), System.nanoTime()+"");
        System.out.println("/printer/printIndex = " + result);

        // 图片打印
//        result = RequestMethod.getInstance().picturePrintIndex(accessToken, machine_code, "http://jeequan.oss-cn-beijing.aliyuncs.com/jeepay/img/jeepay_logo.png", SeqKit.genMhoRefundOrderId());
//        System.out.println("/pictureprint/index = " + result);

        // 取消全部打印
//        result = RequestMethod.getInstance().printCancelAll(accessToken, machine_code);
//        System.out.println("/printer/cancelall = " + result);

        // K8关键词设置
        JSONArray array = new JSONArray();
        array.add("计全支付");
        result = RequestMethod.getInstance().printerSetKeywords(accessToken, machine_code, "order_payment", "white_list", array.toJSONString());
//        System.out.println("/printer/setkeywords = " + result);

    }


    /** 支付下单推送信息 **/
    @RequestMapping("/pay/{configId}")
    public JSONObject pay(@PathVariable("configId") String configId) {

        String logPrefix = "易联云K8支付";
        JSONObject result = new JSONObject();
        result.put("message", "ok");
        JSONObject reqParamJSON = getReqParamJSON();

        log.info("{} 推送消息：{}", logPrefix, reqParamJSON);
        if(reqParamJSON == null || reqParamJSON.isEmpty()) {
            return result;
        }
        String ciphertext = reqParamJSON.getString("ciphertext");
        String nonce = reqParamJSON.getString("nonce");
        String tag = reqParamJSON.getString("tag");
        String iv = reqParamJSON.getString("iv");
        String additional_data = reqParamJSON.getString("additional_data");
        String signature = reqParamJSON.getString("signature");

        try {

            // 查询对应打印设备厂商
            DeviceProvideConfig provideConfig = deviceProvideConfigService.getOne(DeviceProvideConfig.gw()
                    .eq(DeviceProvideConfig::getConfigId, configId)
                    .eq(DeviceProvideConfig::getDeviceType, DeviceProvideConfig.DEVICE_TYPE_PRINTER)
                    .eq(DeviceProvideConfig::getState, CS.YES)
                    .eq(DeviceProvideConfig::getProvider, CS.DEVICE_PROVIDER.YLY)
            );

            if (provideConfig == null || StringUtils.isEmpty(provideConfig.getProviderParams())) {
                log.info("{} 厂商信息不存在：configId={}", logPrefix, configId);
                return result;
            }

            // 厂商信息获取
            YlyParams.ProviderParams providerParams = getParams(JSONObject.parseObject(provideConfig.getProviderParams()));

            // 验签
            boolean verify = verify(providerParams.getPlatPublicKey(), ciphertext, signature);

            if (!verify) {
                log.info("{} 验签失败：configId={}", logPrefix, configId);
                return result;
            }

            // 解密key
            String key = providerParams.getPushPublicKey();
            String plaintext = decrypte(Base64.decodeBase64String(ciphertext), key.getBytes(), iv.getBytes(), Base64.decodeBase64String(tag));
            log.info("{} 解密文本：{}", logPrefix, plaintext);

            // 获取参数
            JSONObject payParams = JSONObject.parseObject(plaintext);
            // 终端号
            String machineCode = payParams.getString("machine_code");

            // 设备验证是否存在
            MchStoreDevice device = mchStoreDeviceService.getOne(MchStoreDevice.gw()
                    .eq(MchStoreDevice::getDeviceNo, machineCode)
                    .eq(MchStoreDevice::getDeviceType, MchStoreDevice.DEVICE_TYPE_PRINTER)
                    .eq(MchStoreDevice::getState, CS.YES)
            );
            if (device == null) {
                log.info("{} 设备信息不存在：deviceId={}", logPrefix, machineCode);
                return result;
            }

            if (StringUtils.isEmpty(device.getMchNo()) || StringUtils.isEmpty(device.getAppId()) || device.getStoreId() == null) {
                log.info("{} 设备未绑定，请先绑定：deviceId={}", logPrefix, machineCode);
                return result;
            }

            ApiRes apiRes = doPayNew(payParams, device);
            log.info("{} >> pay >> 下单返回参数.ApiRes={}", logPrefix, apiRes);
            if (apiRes.getCode().equals(ApiCodeEnum.CUSTOM_FAIL.getCode())) {
                log.info("{} 下单失败：apiRes={}", logPrefix, apiRes);
                return result;
            }
            UnifiedOrderRS bizRs = (UnifiedOrderRS) apiRes.getData();
            log.info("{} >> pay >> 下单响应参数 bizRs = {}", logPrefix, JSON.toJSON(bizRs));

            if (bizRs.getOrderState() == PayOrder.STATE_SUCCESS) {
                return result;
            }
        } catch (BizException e) {
            log.error("{} >> pos >> 打印机下单异常：", logPrefix, e);
            return result;
        } catch (Exception e) {
            log.error("{} >> pos >> 打印机下单异常：", logPrefix, e);
            return result;
        }
        return result;
    }

    private YlyParams.ProviderParams getParams(JSONObject deviceParams) {
        YlyParams.ProviderParams providerParams = JSON.parseObject(deviceParams.toJSONString(), YlyParams.ProviderParams.class);
        providerParams.setPlatPublicKey(deviceParams.getString("platPublicKey"));
        return providerParams;
    }

    /** 构建下单参数 **/
    private ApiRes doPayNew(JSONObject payParams, MchStoreDevice device) {
        // 终端号
        String machineCode = payParams.getString("machine_code");
        // IP
        String ip = payParams.getString("ip");
        // 下单信息
        JSONObject orderPayment = payParams.getJSONObject("order_payment");
        // 付款码
        String authCode = orderPayment.getString("scanned_code");
        // 下单金额
        String payAmount = orderPayment.getString("pay_amount");
        // 来源参数 1：USB键盘 2：USB订单 3：网络金额
        String payAmountSource = orderPayment.getString("pay_amount_source");

        // 下单参数
        String amount = AmountUtil.convertDollar2Cent(payAmount);
        String wayCode = JeepayKit.getPayWayCodeByBarCode(authCode);

        UnifiedOrderRQ unifiedOrderRQ = new UnifiedOrderRQ();
        // 商户号
        unifiedOrderRQ.setMchNo(device.getMchNo());
        unifiedOrderRQ.setAppId(device.getAppId());
        unifiedOrderRQ.setStoreId(device.getStoreId());
        // 商户单号特殊处理
        unifiedOrderRQ.setMchOrderNo(SeqKit.genMhoOrderId());
        unifiedOrderRQ.setWayCode(wayCode);
        unifiedOrderRQ.setAmount(Long.valueOf(amount));
        unifiedOrderRQ.setCurrency("CNY");
        unifiedOrderRQ.setClientIp(getClientIp());
        unifiedOrderRQ.setSubject("标题");
        unifiedOrderRQ.setBody("商品信息");
        unifiedOrderRQ.setSignType(MchApp.SIGN_TYPE_MD5);
        // DBApplicationConfig dbApplicationConfig = sysConfigService.getDBApplicationConfig();
        // 回调地址
        // unifiedOrderRQ.setNotifyUrl(dbApplicationConfig.getMchSiteUrl() + "/api/anon/paytestNotify/payOrder");
        // 设置扩展参数
        JSONObject extParams = new JSONObject();
        extParams.put("authCode", authCode.trim());
        unifiedOrderRQ.setChannelExtra(extParams.toString());

        DeviceInfo deviceInfo = new DeviceInfo();
        deviceInfo.setDeviceType(PayOrder.DEVICE_TYPE_PRINTER);
        deviceInfo.setDeviceNo(device.getDeviceNo());
        deviceInfo.setProvider(device.getProvider());
        // 设备信息
        unifiedOrderRQ.setDeviceInfo(JSON.toJSONString(deviceInfo));

        try {
            UnifiedOrderRQ bizRQ = buildBizRQ(unifiedOrderRQ);

            return this.unifiedOrder(bizRQ.getWayCode(), bizRQ);
        } catch (BizException e) {
            throw e;
        } catch (Exception e) {
            throw new BizException(e.getMessage());
        }
    }

    private UnifiedOrderRQ buildBizRQ(UnifiedOrderRQ rq) {
        //支付方式  比如： ali_bar
        String wayCode = rq.getWayCode();

        //jsapi 收银台聚合支付场景 (不校验是否存在payWayCode)
        if (CS.PAY_WAY_CODE.QR_CASHIER.equals(wayCode)) {
            return rq.buildBizRQ();
        }

        //web收银台聚合支付场景 (不校验是否存在payWayCode)
        if (CS.PAY_WAY_CODE.WEB_CASHIER.equals(wayCode)) {
            return rq.buildBizRQ();
        }

        //如果是自动分类条码
        if (CS.PAY_WAY_CODE.AUTO_BAR.equals(wayCode)) {

            AutoBarOrderRQ bizRQ = (AutoBarOrderRQ) rq.buildBizRQ();
            wayCode = JeepayKit.getPayWayCodeByBarCode(bizRQ.getAuthCode());
            rq.setWayCode(wayCode.trim());
        }

        if (payWayService.count(PayWay.gw().eq(PayWay::getWayCode, wayCode)) <= 0) {
            throw new BizException("不支持的支付方式");
        }

        //转换为 bizRQ
        return rq.buildBizRQ();
    }

    private static boolean verify(String publicKeyStr, String originStr, String encrypted) {
        try {
            byte[] decode = Base64.decodeBase64String(encrypted);
            byte[] data = originStr.getBytes();
            X509EncodedKeySpec keySpec = new X509EncodedKeySpec(Base64.decodeBase64String(publicKeyStr));
            KeyFactory keyFactory = KeyFactory.getInstance("RSA");
            PublicKey publicKey = keyFactory.generatePublic(keySpec);
            Signature sha256withRSA = Signature.getInstance("SHA256withRSA");
            sha256withRSA.initVerify(publicKey);
            sha256withRSA.update(data);
            return sha256withRSA.verify(decode);
        }catch (Exception e) {
            log.error("设备验签失败：", e);
            return false;
        }
    }

}