package com.crm.pay.service.impl.pay;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.entity.property.PraxisPropertyEntity;
import com.crm.pay.service.CustomInfoService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.PraxisService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.entity.finance.deposit.FinanceDepositAddEntity;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.rely.backend.model.pojo.table.CustomInfoTable;
import com.crm.rely.backend.model.pojo.table.FinanceDepositTable;
import com.crm.rely.backend.model.pojo.table.SysRemittanceChannelTable;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.util.AESUtil;
import com.crm.rely.backend.util.DateUtil;
import com.crm.rely.backend.util.HttpUtil;
import com.crm.rely.backend.util.ShaUtil;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import org.jsoup.Connection;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2024-05-24 11:45
 */
@Slf4j
@Service
public class PraxisServiceImpl extends BasePayServiceImpl implements PraxisService {

    private static Map<String, String> LocalesMap = null;
    @Autowired
    private FinanceDepositService financeDepositService;
    @Autowired
    private CustomInfoService customInfoService;

    public static void main(String[] args) {
        System.out.println(AESUtil.encrypt("{\n" +
                "    \"merchantId\":\"API-cwgmarkets\",\n" +
                "    \"applicationKey\":\"cwgmarkets.com\",\n" +
                "    \"merchantSecret\":\"Bn6ls04pfXHn1odMYNUkLwTcxv0um4sG\",\n" +
                "    \"callbackUrl\":\"https://pay.cwgvu.com/praxis/callback\",\n" +
                "    \"pickupUrl\":\"https://pay.cwgvu.com/praxis/pickup\",\n" +
                "    \"version\":\"1.3\",\n" +
                "    \"baseUrl\":\"https://gw.praxisgate.com\",\n" +
                "    \"payUrl\":\"/cashier/cashier\"\n" +
                "}", "bfa5559109f94c78af615bcf00d52060"));
        System.out.println(AESUtil.decrypt("yi/5CoioHqnlpsoejjQW54KVOCMcmm9cET9xF4/G6JMT/gskOgq33eL7pDNu11i" +
                "/BusEiP2FItEONIjQ4sMDqxOGYlAlNSLnM0wkU1fJXgiCLp8WulnTHEs49SrIivMZ4EriRO9d118+BNnqxYoK02xYH08VK" +
                "+U46xIVxju8sMKKrilCSHV6RYejiNRTpIPSugOSlfPB7hy6Q4DJEh3bwzC8NnKy4UMOU" +
                "/haeNT8e7WRbhbIa02XslCHEmkOCUGjxv8n5DdZlG9szMgJUh" +
                "+F0MjsJrFjEkKCTNHlneEfp6eRsnSJOslVFJyK8FYmKuZ3FTdB5rkigfGRvOR25ozEI5vGy7ZUWqPW1xoXBW1eh34D4xsuN8Iu4uWrTVPhQZ676RCWHDC0aUl+luALgs8ubJwFa/XoSEcqXqCqCCg3EsW/TxhjyKLZZ3UyAJLpZkPIgH9a+trOqPc3EVGJSi8RCy93b7FHQsI0T5QKoGJK2Io=", "bfa5559109f94c78af615bcf00d52060"));

//        String content = "API-cwgmarketscgwmarkets" +
//                ".com17165920767fece9c4df5fd15e0d2caea971c21306PD202405250704452580_667538512057734USD10001" +
//                ".000000I8K0MFtcLtOcPzuKZVV3RHnF4xnQM5XU";
//
//        String _sign =
//                "1f6af9ad09e72b75ca01cac4fbd136599296022698e29b05c126a34aad7fc71c991bb22b6eb3df34aee3be9b0f85499f";
//        try {
//            String sign = ShaUtil.sha384(content);
//            System.out.println(sign);
//
//            System.out.println(_sign.equals(sign));
//        } catch (Exception e) {
//            throw new RuntimeException(e);
//        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String pay(FinanceDepositAddEntity balanceSaveEntity) throws Exception {

        //通道配置
        SysRemittanceChannelTable channel = getPayChannel(PayConstants.PRAXIS_PAY_KEY);
        //通道配置实体
        PraxisPropertyEntity payProperty = getPayProperty(channel, PraxisPropertyEntity.class);

        FinanceDepositTable balanceTable = financeDepositService.save(balanceSaveEntity, channel);

        String url = getUrl(payProperty, balanceTable);
        balanceTable.setFormUrl(url);
        
        return url;
    }

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Object> callback(Map<String, String> callbackEntity) throws Exception {


        JSONObject customer = JSON.parseObject(callbackEntity.get("customer"));
        JSONObject session = JSON.parseObject(callbackEntity.get("session"));
        JSONObject transaction = JSON.parseObject(callbackEntity.get("transaction"));

        log.info(String.format("praxis callback orderNo:%s,callback data:%s", session.getString("order_id"),
                JSON.toJSONString(callbackEntity)));
        //获取配置
        PraxisPropertyEntity payProperty = getPayProperty(PayConstants.PRAXIS_PAY_KEY, false,
                PraxisPropertyEntity.class);


        Map<String, Object> mapResult = new HashMap<>(4);
        mapResult.put("version", payProperty.getVersion());
        //验证传输数据
        boolean flag = validate(callbackEntity, session, customer, transaction, payProperty);
        if (!flag) {


            mapResult.put("timestamp", new Date().getTime() / 1000);
            mapResult.put("status", 1);
            mapResult.put("description", "validate fail");
            return mapResult;
        }
//
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(PayConstants.PRAXIS_SUCCESS_STATUS,
                        session.getString("order_id"),
                        transaction.getString("tid"),
                        transaction.getString("transaction_status"), callbackEntity);

        BigDecimal amount = new BigDecimal(transaction.getString("amount"));

        balanceCallbackUpdateEntity.setAmount(amount.divide(new BigDecimal(100), 2, BigDecimal.ROUND_HALF_DOWN));
        /**
         * 更新数据
         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);

        mapResult.put("timestamp", new Date().getTime() / 1000);
        mapResult.put("status", 0);
        mapResult.put("description", "success");
        return mapResult;
    }

    /**
     * 获取支付url
     *
     * @param balanceTable
     * @return
     * @throws ServiceException
     */
    private String getUrl(PraxisPropertyEntity propertyEntity, FinanceDepositTable balanceTable) throws Exception {


        /**
         * {
         *     "merchant_id": "API-cwgmarkets",
         *     "application_key": "cgwmarkets.com",
         *     "intent": "payment",
         *     "currency": "USD",
         *     "amount": 100,
         *     "cid": "700101",
         *     "locale": "en-GB",
         *     "customer_token": null,
         *     "customer_data": {
         *         "country": "GB",
         *         "first_name": "John",
         *         "last_name": "Johnson",
         *         "dob": "12\/31\/1980",
         *         "email": "johnjohnson@example.com",
         *         "phone": "44201112222",
         *         "zip": "WC2N 5DU",
         *         "state": "JS",
         *         "city": "London",
         *         "address": "Random st., 12\/3"
         *     },
         *     "payment_method": null,
         *     "gateway": null,
         *     "validation_url": null,
         *     "notification_url": "https:\/\/api.merchant.com\/v1\/deposits\/tx-1560610955",
         *     "return_url": "https:\/\/merchant.com\/payment_result\/tx-1560610955",
         *     "order_id": "test-1560610955",
         *     "variable1": "your variable",
         *     "variable2": "if that is not enough, you can pass even one more variable",
         *     "variable3": null,
         *     "version": "1.3",
         *     "timestamp": 1716519401
         * }
         */
        CustomInfoTable customInfoTable = customInfoService.getById(balanceTable.getCustomId());

        String orderNo = balanceTable.getSerial() + "_" + balanceTable.getLogin();
        Map<String, Object> params = new HashMap<>(10);

        params.put("merchant_id", propertyEntity.getMerchantId());
        params.put("application_key", propertyEntity.getApplicationKey());
        params.put("intent", "payment");
        params.put("currency", balanceTable.getCurrency());
        params.put("amount", String.format("%.0f", balanceTable.getAmount().doubleValue() * 100));
        params.put("cid", String.valueOf(balanceTable.getCId()));
        params.put("locale", getLocales(balanceTable.getCountry()));
        params.put("customer_token", null);

        Map<String, String> customerParams = new HashMap<>(10);
        customerParams.put("country", customInfoTable.getCountry());
        customerParams.put("first_name", customInfoTable.getFirstName());
        customerParams.put("last_name", customInfoTable.getLastName());
        customerParams.put("dob", customInfoTable.getBirth() == null ? "" : DateUtil.format("MM/dd/yyyy",
                customInfoTable.getBirth()));
        customerParams.put("email", customInfoTable.getEmail());
        customerParams.put("phone", customInfoTable.getPhone());
        customerParams.put("zip", customInfoTable.getZipCode());
        customerParams.put("state", customInfoTable.getState());
        customerParams.put("city", customInfoTable.getCity());
        customerParams.put("address", Strings.isNullOrEmpty(customInfoTable.getAddressLine()) ? "" :
                customInfoTable.getAddressLine().split("@#")[0]);
        params.put("customer_data", customerParams);

        params.put("notification_url", propertyEntity.getCallbackUrl());
        params.put("return_url", propertyEntity.getPickupUrl());
        params.put("order_id", orderNo);
        params.put("version", propertyEntity.getVersion());
        params.put("timestamp", balanceTable.getAddTime().getTime() / 1000);

        String sign = getSign(propertyEntity, params);

        Map<String, String> headers = new HashMap<>(2);
        headers.put("Gt-Authentication", sign);
        headers.put("Content-Type", "application/json; charset=utf-8");
        String payUrl = propertyEntity.getBaseUrl() + propertyEntity.getPayUrl();
        Connection.Response response = HttpUtil.post(payUrl
                , headers, params);

        log.info(String.format("url:%s,json:%s,body:%s", payUrl,
                JSON.toJSON(params), response.body()));
        JSONObject requestEntity = JSON.parseObject(response.body());

        if (requestEntity == null) {
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        } else if ((requestEntity.get("status") != null && !requestEntity.get("status").equals(0))) {
            throw new PayValidatedException(requestEntity.get("description").toString());
        }

        String url = String.valueOf(requestEntity.get("redirect_url"));
        log.info(String.format("praxis orderNo:%s,url:%s", orderNo, url));
        return url;
    }

    /**
     * 验证回调参数是否被篡改
     *
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private boolean validate(Map<String, String> callbackEntity, JSONObject session, JSONObject customer,
                             JSONObject transaction, PraxisPropertyEntity payProperty) throws Exception {

        /**
         * {
         *     "merchant_id": "Test-Integration-Merchant",
         *     "application_key": "Sandbox",
         *     "conversion_rate": 1.000000,
         *     "customer": {
         *         "customer_token": "87cfb23a8f1e68e162c276b754d9c061",
         *         "country": "GB",
         *         "first_name": "John",
         *         "last_name": "Johnson",
         *         "avs_alert": 0,
         *         "verification_alert": null
         *     },
         *     "session": {
         *         "auth_token": "8a7sd87a8sd778ac961062c6bedddb8",
         *         "intent": "payment",
         *         "session_status": "created",
         *         "order_id": "test-1560610955",
         *         "currency": "EUR",
         *         "amount": 100,
         *         "conversion_rate": 1.000000,
         *         "processed_currency": "EUR",
         *         "processed_amount": 100,
         *         "payment_method": "Credit Card",
         *         "gateway": null,
         *         "pin": "1",
         *         "variable1": "your variable",
         *         "variable2": "if that is not enough, you can pass even one more variable",
         *         "variable3": null
         *     },
         *     "transaction": {
         *         "transaction_type": "sale",
         *         "transaction_status": "approved",
         *         "tid": 756850,
         *         "transaction_id": "13348",
         *         "currency": "EUR",
         *         "amount": 100,
         *         "conversion_rate": 1.000000,
         *         "processed_currency": "EUR",
         *         "processed_amount": 100,
         *         "fee": 0,
         *         "fee_included": 0,
         *         "fee_type": "flat",
         *         "payment_method": "Credit Card",
         *         "payment_processor": "TestCardProcessor",
         *         "gateway": "s-pTSZyK23E1Ee5KZpcNbX_aFl0HuhQ0",
         *         "card": {
         *             "card_token": "J-4-a0vPhjZ9R75JP98VDUFgbh9y8sYr",
         *             "card_type": "VISA",
         *             "card_number": "411111******1111",
         *             "card_exp": "12\/2024",
         *             "card_issuer_name": "Bank of Somewhere",
         *             "card_issuer_country": "GB",
         *             "card_holder": "John Johnson"
         *         },
         *         "wallet": null,
         *         "is_async": 0,
         *         "is_cascade": 0,
         *         "cascade_level": 0,
         *         "reference_id": null,
         *         "withdrawal_request_id": null,
         *         "created_by": "INTERNET",
         *         "edited_by": "INTERNET",
         *         "status_code": "SC-002",
         *         "status_details": "Transaction approved"
         *     },
         *     "version": "1.3",
         *     "timestamp": 1590611635
         * }
         */
        String _sign = callbackEntity.get("sign").toString();
        String sign = getCallbackSign(payProperty, callbackEntity, session, customer, transaction);

        log.info(String.format("order no:%s callback result sign compare--callback sign:%s, encryption sign result:%s"
                , session.getString("order_id"), _sign, sign));
        return sign.equals(_sign);
    }

    /**
     * 支付请求加密sign
     *
     * @param propertyEntity
     * @return
     * @throws ServiceException
     */
    private String getSign(PraxisPropertyEntity propertyEntity, Map<String, Object> params) throws Exception {

        String signString = propertyEntity.getMerchantId() + propertyEntity.getApplicationKey() + params.get(
                "timestamp") + params.get("intent") + params.get("cid") + params.get("order_id") + propertyEntity.getMerchantSecret();
        String sign = ShaUtil.sha384(signString);
        String order = params.get("order_id").toString();
        log.info(String.format("praxis pay--orderNo:%s,signString:%s,sign:%s", order, signString, sign));
        return sign;
    }

    /**
     * 回调请求加密sign
     *
     * @param propertyEntity
     * @param callbackEntity
     * @return
     * @throws ServiceException
     */
    private String getCallbackSign(PraxisPropertyEntity propertyEntity, Map<String, String> callbackEntity,
                                   JSONObject session, JSONObject customer, JSONObject transaction) throws Exception {
        /**
         * merchant_id
         * application_key
         * timestamp
         * customer.customer_token
         * session.order_id
         * transaction.tid
         * transaction.currency
         * transaction.amount
         * transaction.conversion_rate
         * transaction.processed_currency
         * transaction.processed_amount
         */
        String signString = propertyEntity.getMerchantId() + propertyEntity.getApplicationKey() + callbackEntity.get(
                "timestamp") + customer.getOrDefault("customer_token", "") + session.getOrDefault("order_id", "") + transaction.getOrDefault("tid", "") + transaction.getOrDefault("currency", "") + transaction.getOrDefault("amount", "") + transaction.getOrDefault("conversion_rate", "") + transaction.getOrDefault("processed_currency", "") + transaction.getOrDefault("processed_amount", "") + propertyEntity.getMerchantSecret();
        String sign = ShaUtil.sha384(signString);
        String order = session.get("order_id").toString();
        log.info(String.format("praxis pay--orderNo:%s,signString:%s,sign:%s", order, signString, sign));
        return sign;
    }

    private String getLocales(String country) {
        if (LocalesMap == null) {
            LocalesMap = new LinkedHashMap<>(30);
            LocalesMap.put("SA", "ar-SA");
            LocalesMap.put("CZ", "cs-CZ");
            LocalesMap.put("CN", "zh-CN");
            LocalesMap.put("TW", "zh-TW");
            LocalesMap.put("NL", "nl-NL");
            LocalesMap.put("GB", "en-GB");
            LocalesMap.put("PH", "tl-PH");
            LocalesMap.put("FI", "fi-FI");
            LocalesMap.put("FR", "fr-FR");
            LocalesMap.put("DE", "de-DE");
            LocalesMap.put("GR", "el-GR");
            LocalesMap.put("IL", "he-IL");
            LocalesMap.put("IN", "hi-IN");
            LocalesMap.put("HU", "hu-HU");
            LocalesMap.put("IT", "it-IT");
            LocalesMap.put("ID", "id-ID");
            LocalesMap.put("JP", "ja-JP");
            LocalesMap.put("KR", "ko-KR");
            LocalesMap.put("LT", "lt-LT");
            LocalesMap.put("MY", "ms-MY");
            LocalesMap.put("PL", "pl-PL");
            LocalesMap.put("PT", "pt-PT");
            LocalesMap.put("RU", "ru-RU");
            LocalesMap.put("SK", "sk-SK");
            LocalesMap.put("ES", "es-ES");
            LocalesMap.put("SE", "sv-SE");
            LocalesMap.put("TH", "th-TH");
            LocalesMap.put("TR", "tr-TR");
            LocalesMap.put("VN", "vi-VN");
            LocalesMap.put("AM", "hy-AM");
            LocalesMap.put("BG", "bg-BG");
            LocalesMap.put("KH", "kh-KH");
            LocalesMap.put("DK", "da-DK");
            LocalesMap.put("EE", "et-EE");
            LocalesMap.put("GE", "ka-GE");
            LocalesMap.put("LV", "lv-LV");
            LocalesMap.put("NO", "nn-NO");
            LocalesMap.put("RS", "sr-RS");
            LocalesMap.put("SI", "sl-SI");
            LocalesMap.put("IN", "ta-IN");
            LocalesMap.put("UA", "uk-UA");
            LocalesMap.put("AL", "sq-AL");
        }
        String locales;
        locales = "en-GB";
//        if (Strings.isNullOrEmpty(country)) {
//            locales = "en-GB";
//        } else {
//            locales = LocalesMap.get(country);
//        }

        if (Strings.isNullOrEmpty(locales)) {
            locales = "en-GB";
        }
        return locales;
    }
}
