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

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.crm.pay.dto.finance.withdraw.automatic.FinanceWithdrawProxpayDto;
import com.crm.pay.entity.callback.prox.ProxpayCallbackEntity;
import com.crm.pay.entity.property.ProxpayPropertyEntity;
import com.crm.pay.entity.property.ProxpayWithdrawPropertyEntity;
import com.crm.pay.service.CustomInfoService;
import com.crm.pay.service.FinanceDepositService;
import com.crm.pay.service.FinanceWithdrawService;
import com.crm.pay.service.ProxpayService;
import com.crm.pay.service.impl.base.BasePayServiceImpl;
import com.crm.rely.backend.core.constant.Constants;
import com.crm.rely.backend.model.constant.PayConstants;
import com.crm.rely.backend.model.dto.finance.withdraw.FinanceWithdrawDto;
import com.crm.rely.backend.model.entity.finance.deposit.FinanceDepositAddEntity;
import com.crm.pay.entity.FinanceDepositCallbackUpdateEntity;
import com.crm.pay.entity.finance.withdraw.FinanceWithdrawApproveChannelSubmitEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawInfoEntity;
import com.crm.pay.entity.finance.withdraw.automatic.FinanceWithdrawProxpayEntity;
import com.crm.rely.backend.core.exception.PayValidatedException;
import com.crm.rely.backend.core.exception.ServiceException;
import com.crm.rely.backend.model.pojo.table.*;
import com.crm.rely.backend.util.*;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @program:
 * @description:
 * @author: houn
 * @create: 2024-09-23 11:45
 */
@Slf4j
@Service
public class
ProxpayServiceImpl extends BasePayServiceImpl implements ProxpayService {
    @Autowired
    private FinanceDepositService financeDepositService;

    @Autowired
    private FinanceWithdrawService financeWithdrawService;

   /* @Autowired
    private FinanceAgentWithdrawService financeAgentWithdrawService;*/
    @Autowired
    private CustomInfoService customInfoService;


    @Value("${web.upload-path:./upload}")
    private String UPLOAD;

    public static void main(String[] args) throws IOException {
        System.out.println(AESUtil.decrypt("", "bfa5559109f94c78af615bcf00d52060"));
//
        System.out.println(AESUtil.encrypt("", "bfa5559109f94c78af615bcf00d52060"));

        Map<String, Object> params = new HashMap<>(10);

        params.put("amount", new BigDecimal("646.0000"));
        params.put("refno", "123123");
        params.put("merchant_id", Integer.valueOf("48"));

        System.out.println(JSON.toJSONString(params));


    }

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

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

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

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

    @Override
    public FinanceWithdrawProxpayDto withdrawInfo(FinanceWithdrawInfoEntity entity) throws Exception {
        FinanceWithdrawDto withdrawDto = null;
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            withdrawDto = financeWithdrawService.searchSingle(entity.getId());

        } else {
            //withdrawDto = financeAgentWithdrawService.searchSingle(entity.getId());
        }
        entity.setPayType(PayConstants.PROXPAY_PAY_TYPE_REMIT);

        SysRemitChannelTable withdrawChannel = getWithdrawChannel(entity.getPayType());
        FinanceWithdrawProxpayDto dto = new FinanceWithdrawProxpayDto();
        dto.setAmount(withdrawDto.getTransformAmount() == null ? null :
                withdrawDto.getTransformAmount().setScale(2, BigDecimal.ROUND_DOWN));
        dto.setBankName(withdrawDto.getBankName());
        dto.setBookBankBranch(withdrawDto.getBankBranchName());
        dto.setName(withdrawDto.getName());
        dto.setPreferCurrency(withdrawDto.getTransformCurrency());
        dto.setBookBankNumber(withdrawDto.getBankCardNum());
        dto.setBookBankName(withdrawDto.getBankUname());
        dto.setRefCustomerId(String.valueOf(withdrawDto.getCId()));
        dto.setId(withdrawDto.getId());
        dto.setWithdrawUrl(withdrawChannel.getWithdrawUrl());
        dto.setSerial(withdrawDto.getSerial());
        dto.setPayType(entity.getPayType());
        return dto;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String withdraw(FinanceWithdrawProxpayEntity entity) throws Exception {


        FinanceWithdrawApproveChannelSubmitEntity financeWithdrawApproveChannelSubmitEntity =
                new FinanceWithdrawApproveChannelSubmitEntity();
        financeWithdrawApproveChannelSubmitEntity.setSubmitStatus(PayConstants.PASSED_STATUS);
        financeWithdrawApproveChannelSubmitEntity.setId(entity.getId());
        financeWithdrawApproveChannelSubmitEntity.setActualPayType(entity.getPayType());

        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            financeWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);

        } else {
            //financeAgentWithdrawService.approve(financeWithdrawApproveChannelSubmitEntity);
        }

        FinanceWithdrawDto withdrawDto = null;
        if (entity.getWithdrawInfoType() == null || entity.getWithdrawInfoType().equals(1)) {
            withdrawDto = financeWithdrawService.searchSingle(entity.getId());

        } else {
            //withdrawDto = financeAgentWithdrawService.searchSingle(entity.getId());
        }

        entity.setCId(withdrawDto.getCId());
        entity.setSerial(withdrawDto.getSerial());
        entity.setLogin(withdrawDto.getLogin());


        ProxpayWithdrawPropertyEntity proxpayWithdrawProperty = getWithdrawProperty(PayConstants.PROXPAY_PAY_TYPE_REMIT,
                ProxpayWithdrawPropertyEntity.class);
        String accessToken = getAccessToken(proxpayWithdrawProperty);

        int id = getCustomer(withdrawDto.getCId(), proxpayWithdrawProperty, accessToken);

        if (id <= 0) {
            id = createCustomer(entity, proxpayWithdrawProperty, accessToken);
        } else {
            updateCustomer(entity, proxpayWithdrawProperty, accessToken, id);
        }

        withdraw(entity, proxpayWithdrawProperty, accessToken, id);
        return null;
    }

    /**
     * 回调处理数据
     *
     * @param callbackEntity
     * @throws ServiceException
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void callback(ProxpayCallbackEntity callbackEntity) throws Exception {

        log.info(String.format("callback orderNo:%s,callback data:%s",
                callbackEntity.getRefno(), JSON.toJSONString(callbackEntity)));

//        if (!validate(callbackEntity)) {
//            throw new ServiceException("fail");
//        }

        List<String> success = new ArrayList<>(2);
        success.add(PayConstants.PROXPAY_CP_SUCCESS_STATUS);
        success.add(PayConstants.PROXPAY_Y_SUCCESS_STATUS);
        FinanceDepositSerialMappingTable financeDepositSerialMappingTable = getById(callbackEntity.getRefno(),
                PayConstants.PROXPAY_PAY_TYPE);

//通道配置
        FinanceDepositCallbackUpdateEntity balanceCallbackUpdateEntity =
                getFinanceDepositCallbackUpdateEntity(success,
                        financeDepositSerialMappingTable.getDepositSerial(),
                        callbackEntity.getOrderno(),
                        callbackEntity.getStatus(), callbackEntity);

        if (!Strings.isNullOrEmpty(callbackEntity.getTotal())) {
            balanceCallbackUpdateEntity.setTransformAmount(new BigDecimal(callbackEntity.getTotal()));

        }

//        /**
//         * 更新数据
//         */
        financeDepositService.updateCallback(balanceCallbackUpdateEntity);

        if (success.contains(callbackEntity.getStatus())) {
            callbackProx(callbackEntity);
        }

    }

    private void callbackProx(ProxpayCallbackEntity callbackEntity) throws Exception {
        /**
         * curl --location 'https://api.proxpay.co/spark/transactions/deposits' \
         * --header 'apikey: C34rn#5Kf4xz' \
         * --header 'Content-Type: application/json' \
         * --header 'Authorization: ••••••' \
         * --data '{
         *     "amount": 10260.00,
         *     "refno": "1000043",
         *     "merchant_id": 48
         *
         * }'
         */
        ProxpayPropertyEntity propertyEntity = getPayProperty(PayConstants.PROXPAY_PAY_TYPE,
                ProxpayPropertyEntity.class);


        Map<String, Object> params = new HashMap<>(10);

        params.put("amount", new BigDecimal(callbackEntity.getTotal()));
        params.put("refno", callbackEntity.getRefno());
        params.put("merchant_id", Integer.valueOf(propertyEntity.getProxpayMerchantId()));

        Map<String, String> headers = new HashMap<>(5);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("Authorization", propertyEntity.getBearer() + " " + getAccessToken(propertyEntity));
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");


        String proxpayUrl = propertyEntity.getProxpayUrl();

        log.info(String.format("order:%s, callback Prox send rul:%s,params:%s,headers:%s",
                params.get("refno"), proxpayUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));

        Connection.Response response = HttpUtil.post(proxpayUrl, headers, JSON.toJSONString(params));

        log.info(String.format("order:%s,callback Prox response status code:%d, response body is:%s",
                params.get("refno"), response.statusCode(),
                response.body()));

        JSONObject re = JSON.parseObject(response.body());

//        if (!"SUCCESS".equals(re.getString("status"))) {
//            throw ServiceException.exception(Constants.SYSTEM_ERROR);
//        }

    }

    private String payment(ProxpayPropertyEntity propertyEntity,
                           FinanceDepositTable financeDepositTable) throws Exception {

        String serial = financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin();
        FinanceDepositSerialMappingTable financeDepositSerialMappingTable = saveMapping(serial,
                PayConstants.PROXPAY_PAY_TYPE);

//        String loginString = String.valueOf(financeDepositTable.getLogin());
        String referenceNo =
                String.valueOf(financeDepositSerialMappingTable.getId());

        CustomInfoTable customInfoTable = customInfoService.getById(financeDepositTable.getCustomId());

        if (customInfoTable == null) {
            throw ServiceException.exception(Constants.SYSTEM_ERROR);
        }

//        String accessToken = getAccessToken(propertyEntity);

        Map<String, String> params = new HashMap<>(10);

        params.put("total", String.format("%.2f", financeDepositTable.getTransformAmount().doubleValue()));
        params.put("productDetail", String.valueOf(financeDepositTable.getCId()));
        params.put("customerEmail", customInfoTable.getEmail());
        params.put("customerName", financeDepositTable.getName().replace(" ", ""));
        params.put("merchantID", propertyEntity.getMerchantId());
        params.put("referenceNo", referenceNo);

        String paramString = MapUtil.getString(params);


        Map<String, String> headers = new HashMap<>(3);
        headers.put("apikey", propertyEntity.getApiKey());
        headers.put("Authorization", propertyEntity.getBearer() + " " + propertyEntity.getAuthKey());
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");


        String paymentUrl = propertyEntity.getTransactionsDeposits() + "?" + paramString;

        log.info(String.format("order:%s, payment send rul:%s,params:%s,headers:%s",
                params.get("referenceNo"), paymentUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));

        Connection.Response response = HttpUtil.post(paymentUrl, headers, "");

        log.info(String.format("order:%s,payment response body is:%s", params.get("referenceNo"),
                response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());

        if (!jsonObject.getString("status").equals("success")) {
            throw new PayValidatedException(jsonObject.getString("message"));
        }

        String content = jsonObject.getJSONObject("data").getString("image");
        String fileName = UUIDUtil.getUUID() + ".png";
        String middlePath = "proxpay/" + financeDepositTable.getCustomId();
        String savePath = UPLOAD + "/file/" + middlePath;

        ImageBase64ConverterUtil.convertBase64ToFile(content.replace("data:image/png;base64,", ""), savePath, fileName);
        String url = "/file/" + middlePath + "/" + fileName;


        if (Strings.isNullOrEmpty(url)) {
            log.error("payment url is null");
            throw new PayValidatedException("Payment platform is abnormal, please try again later or contact customer" +
                    " service.");
        }

        log.info(String.format("prox,payment orderNo:%s,url:%s",
                financeDepositTable.getSerial() + "_" + financeDepositTable.getLogin(), url));

        return url;

    }

    private int createCustomer(FinanceWithdrawProxpayEntity entity, ProxpayWithdrawPropertyEntity propertyEntity,
                               String accessToken) throws Exception {


/**
 * {
 *   "ref_customer_id": "PSC00111",
 *   "merchant_id": 10,
 *   "name": "Panupong Chantaklang",
 *   "bank_name": "SCB",
 *   "book_bank_number": "2345678901",
 *   "book_bank_name": "Panupong Chantaklang",
 *   "book_bank_branch": "รังสิต"
 * }
 */
        Map<String, Object> params = new HashMap<>(10);

        params.put("ref_customer_id", String.valueOf(entity.getCId()));
        params.put("merchant_id", Integer.valueOf(propertyEntity.getProxpayMerchantId()));
        params.put("name", entity.getName());
        params.put("bank_name", entity.getBankName());
        params.put("book_bank_number", entity.getBookBankNumber());
        params.put("book_bank_name", entity.getBookBankName());
        params.put("book_bank_branch", entity.getBookBankBranch());

        Map<String, String> headers = new HashMap<>(4);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("Authorization", "Bearer " + accessToken);
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");

        String createCustomUrl = propertyEntity.getCreateCustomUrl();

        log.info(String.format(" create custom rul:%s,params:%s,headers:%s", createCustomUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));


        String data = JSON.toJSONString(params);

        Connection.Response response = HttpUtil.post(createCustomUrl, headers, data);

        log.info(String.format("create custom response body is:%s", response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());


        String error = jsonObject.getString("error");
        if (!Strings.isNullOrEmpty(error)) {
            log.info("error:" + error);
            throw ServiceException.exception(error);
        }

        return jsonObject.getIntValue("id");

    }

    private int getCustomer(Long cId, ProxpayWithdrawPropertyEntity propertyEntity,
                            String accessToken) throws Exception {


        Map<String, String> headers = new HashMap<>(4);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("Authorization", "Bearer " + accessToken);
        headers.put("accept", "application/json");

        String customUrl = propertyEntity.getCustomUrl() + String.valueOf(cId);

        log.info(String.format("get custom url::%s,headers:%s", customUrl, JSON.toJSONString(headers)));

        Connection.Response response = HttpUtil.get(customUrl, headers);

        log.info(String.format("get custom  response body is:%s", response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());

        String error = jsonObject.getString("error");
        if (!Strings.isNullOrEmpty(error)) {
            log.info("error:" + error);
            return 0;
        }

        return jsonObject.getIntValue("id");

    }

    private int updateCustomer(FinanceWithdrawProxpayEntity entity, ProxpayWithdrawPropertyEntity propertyEntity,
                               String accessToken, int id) throws Exception {


        /**
         * {
         *   "ref_customer_id": "C0001",
         *   "name": "John Doe",
         *   "bank_name": "SCB",
         *   "book_bank_number": "12341234",
         *   "book_bank_name": "John Doe"
         * }
         */
        Map<String, String> params = new HashMap<>(10);

        params.put("ref_customer_id", String.valueOf(entity.getCId()));
        params.put("name", entity.getName());
//        params.put("bank_name", table.getBankName());
        params.put("bank_name", entity.getBankName());
        params.put("book_bank_number", entity.getBookBankNumber());
        params.put("book_bank_name", entity.getBookBankName());

        Map<String, String> headers = new HashMap<>(4);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("Authorization", "Bearer " + accessToken);
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");


        String customUrl = propertyEntity.getUpdateCustomUrl() + String.valueOf(id);

        log.info(String.format("update custom url::%s,params:%s,headers:%s", customUrl, JSON.toJSONString(params),
                JSON.toJSONString(headers)));

        Connection.Response response = HttpUtil.put(customUrl, headers, params);

        log.info(String.format("update custom  response body is:%s", response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());

        String error = jsonObject.getString("error");
        if (!Strings.isNullOrEmpty(error)) {
            log.info("error:" + error);
            return 0;
        }

        return jsonObject.getIntValue("id");

    }

    private void withdraw(FinanceWithdrawProxpayEntity entity, ProxpayWithdrawPropertyEntity propertyEntity,
                          String accessToken, int id) throws Exception {


        /**
         * {
         *   "amount": 200,
         *   "refno": "PSHQW202201180002",
         *   "merchant_id": 3,
         *   "customer_id": "2 (Only use for Customer Withdrawal)",
         *   "prefer_currency": "USD (Only use for HQ International Withdrawal)"
         * }
         */
        Map<String, Object> params = new HashMap<>(10);

        params.put("amount", entity.getAmount().setScale(2, BigDecimal.ROUND_DOWN));
        params.put("refno", entity.getSerial());
        params.put("merchant_id", Integer.valueOf(propertyEntity.getProxpayMerchantId()));
        params.put("customer_id", id);
        params.put("prefer_currency", entity.getPreferCurrency());

        Map<String, String> headers = new HashMap<>(4);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("Authorization", "Bearer " + accessToken);
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");


        String withdrawUrl = propertyEntity.getWithdrawOrderNoUrl();

        log.info(String.format("withdraw url::%s,params:%s,headers:%s", withdrawUrl, JSON.toJSONString(params),
                JSON.toJSONString(headers)));

        Connection.Response response = HttpUtil.post(withdrawUrl, headers, params);

        log.info(String.format("withdraw  response body is:%s", response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());

        String error = jsonObject.getString("message");

        if (!Strings.isNullOrEmpty(error)) {
            log.info("error:" + error);
            throw ServiceException.exception(error);
        }

        if (!Strings.isNullOrEmpty(jsonObject.getString("error"))) {
            log.info("error:" + jsonObject.getString("error"));
            throw ServiceException.exception(jsonObject.getString("error"));
        }

        return;

    }

    private String getAccessToken(ProxpayPropertyEntity propertyEntity) throws Exception {

        Map<String, String> params = new HashMap<>(10);

        params.put("username", propertyEntity.getUsername());
        params.put("password", propertyEntity.getPassword());

        Map<String, String> headers = new HashMap<>(3);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");

        String userLoginUrl = propertyEntity.getUsersLogin();

        log.info(String.format(" payment send rul:%s,params:%s,headers:%s", userLoginUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));


        String data = JSON.toJSONString(params);

        Connection.Response response = HttpUtil.post(userLoginUrl, headers, data);

        log.info(String.format("getAccessToken response body is:%s", response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());


        String accessToken = jsonObject.getString("access_token");
        if (Strings.isNullOrEmpty(accessToken)) {
            throw new PayValidatedException(jsonObject.getString("error"));
        }

        log.info("accessToken:" + accessToken);
        return accessToken;

    }

    private String getAccessToken(ProxpayWithdrawPropertyEntity propertyEntity) throws Exception {

        Map<String, String> params = new HashMap<>(10);

        params.put("username", propertyEntity.getUsername());
        params.put("password", propertyEntity.getPassword());

        Map<String, String> headers = new HashMap<>(3);
        headers.put("apikey", propertyEntity.getProxpayApiKey());
        headers.put("accept", "application/json");
        headers.put("Content-Type", "application/json");

        String userLoginUrl = propertyEntity.getUsersLogin();

        log.info(String.format(" payment send rul:%s,params:%s,headers:%s", userLoginUrl,
                JSON.toJSONString(params), JSON.toJSONString(headers)));


        String data = JSON.toJSONString(params);

        Connection.Response response = HttpUtil.post(userLoginUrl, headers, data);

        log.info(String.format("getAccessToken response body is:%s", response.body()));


        JSONObject jsonObject = JSON.parseObject(response.body());


        String accessToken = jsonObject.getString("access_token");
        if (Strings.isNullOrEmpty(accessToken)) {
            throw new PayValidatedException(jsonObject.getString("error"));
        }

        log.info("accessToken:" + accessToken);
        return accessToken;

    }


}
