package com.izouma.nineth.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.huifu.adapay.Adapay;
import com.huifu.adapay.core.exception.BaseAdaPayException;
import com.huifu.adapay.model.*;
import com.izouma.nineth.config.AdapayProperties;
import com.izouma.nineth.config.EventNames;
import com.izouma.nineth.config.GeneralProperties;
import com.izouma.nineth.domain.AdapayMerchant;
import com.izouma.nineth.dto.PageQuery;
import com.izouma.nineth.dto.adapay.MemberInfo;
import com.izouma.nineth.dto.adapay.PaymentItem;
import com.izouma.nineth.dto.adapay.PaymentList;
import com.izouma.nineth.dto.adapay.SettleAccountsItem;
import com.izouma.nineth.exception.BusinessException;
import com.izouma.nineth.repo.AdapayMerchantRepo;
import com.izouma.nineth.utils.JpaUtils;
import com.izouma.nineth.utils.ObjUtils;
import com.izouma.nineth.utils.SnowflakeIdWorker;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.MapUtils;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;

@Service
@AllArgsConstructor
@Slf4j
public class AdapayMerchantService {

    private final AdapayMerchantRepo adapayMerchantRepo;
    private final AdapayProperties  adapayProperties;
    private final RocketMQTemplate  rocketMQTemplate;
    private final GeneralProperties generalProperties;

    @PostConstruct
    public void init() {
        log.info("init AdapayMerchantService");
        for (AdapayMerchant merchant : adapayMerchantRepo.findAll()) {
            addMerchant(merchant);
            if (merchant.isSelected()) {
                try {
                    select(merchant.getId());
                    log.info("select merchant success={}", merchant.getName());
                } catch (Exception e) {
                    log.error("select merchant error", e);
                }
            }
        }
    }

    public Page<AdapayMerchant> all(PageQuery pageQuery) {
        return adapayMerchantRepo.findAll(JpaUtils.toSpecification(pageQuery, AdapayMerchant.class), JpaUtils.toPageRequest(pageQuery));
    }

    public AdapayMerchant save(AdapayMerchant record) {
        record = adapayMerchantRepo.save(record);
        addMerchant(record);
        return record;
    }

    public AdapayMerchant update(AdapayMerchant record) {
        if (record.getId() == null) {
            throw new BusinessException("id不能为空");
        }
        AdapayMerchant orig = adapayMerchantRepo.findById(record.getId()).orElseThrow(new BusinessException("无记录"));
        ObjUtils.merge(orig, record);
        record = adapayMerchantRepo.save(orig);
        addMerchant(record);
        return record;
    }


    public void sendSelectEvent(Long id) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("name", EventNames.SWITCH_ACCOUNT);
        jsonObject.put("data", id);
        rocketMQTemplate.convertAndSend(generalProperties.getBroadcastEventTopic(), jsonObject);

        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public void select(Long id) throws Exception {
        AdapayMerchant merchant = adapayMerchantRepo.findById(id).orElseThrow(new BusinessException("商户不存在"));

        MerConfig merConfig = new MerConfig();
        merConfig.setApiKey(merchant.getApiKey());
        merConfig.setApiMockKey(merchant.getMockKey());
        merConfig.setRSAPrivateKey(merchant.getPrivKey());
        merConfig.setRSAPublicKey(merchant.getPublicKey());
        Adapay.initWithMerConfig(merConfig);

        adapayProperties.setApiKey(merchant.getApiKey());
        adapayProperties.setMockKey(merchant.getMockKey());
        adapayProperties.setPrivKey(merchant.getPrivKey());
        adapayProperties.setPublicKey(merchant.getPublicKey());
        adapayProperties.setMerchant(merchant.getName());
        adapayProperties.setAppId(merchant.getAppId());

        List<AdapayMerchant> list = adapayMerchantRepo.findAll();
        list.forEach(m -> m.setSelected(m.getId().equals(id)));
        adapayMerchantRepo.saveAll(list);
    }

    public void addMerchant(AdapayMerchant merchant) {
        try {
            MerConfig merConfig = new MerConfig();
            merConfig.setApiKey(merchant.getApiKey());
            merConfig.setApiMockKey(merchant.getMockKey());
            merConfig.setRSAPrivateKey(merchant.getPrivKey());
            merConfig.setRSAPublicKey(merchant.getPublicKey());
            Adapay.addMerConfig(merConfig, merchant.getName());
        } catch (Exception e) {
            log.error("add Merchant Error", e);
        }
    }

    public void createMemberForAll(String memberId, String tel, String realName, String idno) throws BaseAdaPayException {
        AtomicReference<BaseAdaPayException> ex = new AtomicReference<>();
        adapayMerchantRepo.findAll().parallelStream().forEach(merchant -> {
            try {
                createMember(merchant.getName(), merchant.getAppId(), memberId, tel, realName, idno);
            } catch (BaseAdaPayException e) {
                log.error("createMemberForAll", e);
                ex.set(e);
            }
        });
        if (ex.get() != null) {
            throw ex.get();
        }
    }

    public void createMember(String merchant, String appId, String memberId, String tel, String realName, String idno) throws BaseAdaPayException {
        Map<String, Object> memberParams = new HashMap<>();
        memberParams.put("adapay_func_code", "members.realname");
        memberParams.put("member_id", memberId);
        memberParams.put("app_id", appId);
        memberParams.put("tel_no", tel);
        memberParams.put("user_name", realName);
        memberParams.put("cert_type", "00");
        memberParams.put("cert_id", idno);
        Map<String, Object> res = AdapayCommon.requestAdapay(memberParams, merchant);
        log.info("createMember merchant={} response={}", merchant, JSON.toJSONString(res, SerializerFeature.PrettyFormat));
        if (!("succeeded".equals(MapUtils.getString(res, "status"))
                || "member_id_exists".equals(MapUtils.getString(res, "error_code")))) {
            String errMsg = MapUtils.getString(res, "error_msg");
            String errCode = MapUtils.getString(res, "error_code");
            log.error("createMember error merchant={} memberId={}", merchant, memberId);
            throw new BusinessException(errMsg + "(" + errCode + ")");
        }
    }

    public String createSettleAccountForAll(String memberId, String realName, String idNo, String phone, String bankNo) throws BaseAdaPayException {
        String id = null;
        for (AdapayMerchant merchant : adapayMerchantRepo.findAll()) {
            id = createSettleAccount(merchant.getName(), merchant.getAppId(), memberId, realName, idNo, phone, bankNo);
        }
        return id;
    }

    public String createSettleAccount(String merchant, String appId, String memberId, String realName, String idNo, String phone, String bankNo) throws BaseAdaPayException {
        Map<String, Object> settleCountParams = new HashMap<>();
        Map<String, Object> accountInfo = new HashMap<>();
        accountInfo.put("card_id", bankNo);
        accountInfo.put("card_name", realName);
        accountInfo.put("cert_id", idNo);
        accountInfo.put("cert_type", "00");
        accountInfo.put("tel_no", phone);
        accountInfo.put("bank_acct_type", "2");
        settleCountParams.put("member_id", memberId);
        settleCountParams.put("app_id", appId);
        settleCountParams.put("channel", "bank_account");
        settleCountParams.put("account_info", accountInfo);
        Map<String, Object> res = SettleAccount.create(settleCountParams, merchant);
        log.info("createSettleAccount merchant={} response={}", merchant, JSON.toJSONString(res, SerializerFeature.PrettyFormat));
        if (!("succeeded".equals(MapUtils.getString(res, "status"))
                || "account_exists".equals(MapUtils.getString(res, "error_code")))) {
            String errMsg = MapUtils.getString(res, "error_msg");
            String errCode = MapUtils.getString(res, "error_code");
            log.error("createSettleAccount error merchant={} memberId={}", merchant, memberId);
            throw new BusinessException(errMsg + "(" + errCode + ")");
        }
        return MapUtils.getString(res, "id");
    }

    public void delSettleAccountForAll(String memberId) throws BaseAdaPayException {
        for (AdapayMerchant merchant : adapayMerchantRepo.findAll()) {
            delSettleAccount(merchant.getName(), merchant.getAppId(), memberId);
        }
    }

    public void delSettleAccount(String merchant, String appId, String memberId) throws BaseAdaPayException {
        Map<String, Object> memberParams = new HashMap<>();
        memberParams.put("member_id", memberId);
        memberParams.put("app_id", appId);
        Map<String, Object> member = Member.query(memberParams, merchant);
        log.info("query member, merchant={} member={} res={}",
                merchant, memberId, JSON.toJSONString(member, SerializerFeature.PrettyFormat));
        MemberInfo memberInfo = JSON.parseObject(JSON.toJSONString(member), MemberInfo.class);
        if ("succeeded".equals(memberInfo.getStatus())) {
            if (memberInfo.getSettleAccounts() != null && memberInfo.getSettleAccounts().size() > 0) {
                SettleAccountsItem settleAccountsItem = memberInfo.getSettleAccounts().get(0);
                Map<String, Object> settleCountParams = new HashMap<>();
                settleCountParams.put("settle_account_id", settleAccountsItem.getId());
                settleCountParams.put("member_id", memberId);
                settleCountParams.put("app_id", appId);
                Map<String, Object> settleCount = SettleAccount.delete(settleCountParams, merchant);
                log.info("delSettleAccount, merchant={} member={} res={}",
                        merchant, memberId, JSON.toJSONString(settleCount, SerializerFeature.PrettyFormat));
                checkSuccess(settleCount);
            }
        } else if ("member_not_exists".equals(memberInfo.getErrorCode())) {
            log.info("delSettleAccount member_not_exists, merchant={} member={}", merchant, memberId);
        } else {
            throw new BusinessException(memberInfo.getErrorMsg() + memberInfo.getErrorCode());
        }
    }

    public void queryMembers(String merchant, String appId) {
        boolean hasMore = true;
        while (hasMore) {
            try {
                Map<String, Object> memberParams = new HashMap<>(2);
                memberParams.put("page_index", "1");
                memberParams.put("app_id", appId);
                memberParams.put("page_size", "20");
                memberParams.put("created_gte", String.valueOf(System.currentTimeMillis() - 5 * 60 * 1000));
                memberParams.put("created_lte", String.valueOf(System.currentTimeMillis()));
                Map<String, Object> member = Member.queryList(memberParams);
                MapUtils.getBoolean(member, "has_more", false);
            } catch (Exception e) {
                log.error("queryMembers error", e);
                hasMore = false;
            }
        }
    }

    public Object query(Long merchantId, String id) throws BaseAdaPayException {
        AdapayMerchant merchant = adapayMerchantRepo.findById(merchantId).orElseThrow(new BusinessException("商户不存在"));
        Map<String, Object> paymentParams = new HashMap<>();
        paymentParams.put("app_id", merchant.getAppId());
        paymentParams.put("payment_id", id);
        Map<String, Object> paymentList = Payment.queryList(paymentParams, merchant.getName());
        log.info(JSON.toJSONString(paymentList, SerializerFeature.PrettyFormat));
        return paymentList;
    }

    public Object refund(Long merchantId, String id) throws BaseAdaPayException {
        AdapayMerchant merchant = adapayMerchantRepo.findById(merchantId).orElseThrow(new BusinessException("商户不存在"));

        Map<String, Object> paymentParams = new HashMap<>();
        paymentParams.put("app_id", merchant.getAppId());
        paymentParams.put("payment_id", id);
        Map<String, Object> res = Payment.queryList(paymentParams, merchant.getName());
        log.info(JSON.toJSONString(res, SerializerFeature.PrettyFormat));
        PaymentList paymentList = JSON.parseObject(JSON.toJSONString(res), PaymentList.class);

        if (paymentList.getPayments() != null && paymentList.getPayments().size() == 1) {
            PaymentItem paymentItem = paymentList.getPayments().get(0);
            Map<String, Object> refundParams = new HashMap<>();
            refundParams.put("refund_amt", paymentItem.getPayAmt());
            refundParams.put("refund_order_no", new SnowflakeIdWorker(0, 0).nextId() + "");
            Map<String, Object> response = Refund.create(id, refundParams, merchant.getName());
            log.info(JSON.toJSONString(response, SerializerFeature.PrettyFormat));
            return response;
        }
        return res;
    }

    public static void checkSuccess(Map<String, Object> map) {
        String status = MapUtils.getString(map, "status");
        if (!("succeeded".equals(status))) {
            String errMsg = MapUtils.getString(map, "error_msg");
            String errCode = MapUtils.getString(map, "error_code");
            if ("account_exists".equals(errCode)) {
                return;
            }
            throw new BusinessException(errMsg + "(" + errCode + ")");
        }
    }


}
