package com.jeequan.jeepay.thirdparty.channel.wxpay;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.github.binarywang.wxpay.bean.applyment.ApplymentStateQueryResult;
import com.github.binarywang.wxpay.bean.applyment.WxPayApplyment4SubCreateRequest;
import com.github.binarywang.wxpay.bean.applyment.WxPayApplymentCreateResult;
import com.github.binarywang.wxpay.bean.applyment.enums.BankAccountTypeEnum;
import com.github.binarywang.wxpay.bean.applyment.enums.IdTypeEnum;
import com.github.binarywang.wxpay.bean.applyment.enums.SalesScenesTypeEnum;
import com.github.binarywang.wxpay.bean.applyment.enums.SubjectTypeEnum;
import com.github.binarywang.wxpay.bean.media.ImageUploadResult;
import com.github.binarywang.wxpay.exception.WxPayException;
import com.github.binarywang.wxpay.service.Applyment4SubService;
import com.github.binarywang.wxpay.service.MerchantMediaService;
import com.github.binarywang.wxpay.service.WxPayService;
import com.github.binarywang.wxpay.service.impl.Applyment4SubServiceImpl;
import com.github.binarywang.wxpay.service.impl.MerchantMediaServiceImpl;
import com.jeequan.jeepay.core.constants.CS;
import com.jeequan.jeepay.core.entity.MchApplyment;
import com.jeequan.jeepay.core.interfaces.paychannel.IIsvmchApplymentService;
import com.jeequan.jeepay.core.model.applyment.ApplymentSignInfo;
import com.jeequan.jeepay.core.model.applyment.IdcardInfo;
import com.jeequan.jeepay.core.model.applyment.WxpayApplymentInfo;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvParams;
import com.jeequan.jeepay.core.model.params.wxpay.WxpayIsvsubMchParams;
import com.jeequan.jeepay.core.utils.SpringBeansUtil;
import com.jeequan.jeepay.service.impl.SysConfigService;
import com.jeequan.jeepay.thirdparty.model.WxServiceWrapper;
import com.jeequan.jeepay.thirdparty.service.ConfigContextQueryService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.*;

/***
* 微信接口进件
*
* @author zx
*
* @date 2022/1/4 14:54
*/
@Service
@Slf4j
public class WxpayMchApplymentService implements IIsvmchApplymentService<WxpayApplymentInfo> {

    @Autowired protected SysConfigService sysConfigService;

    @Autowired private ConfigContextQueryService configContextQueryService;


    @Override
    public MchApplyment firstApplyment(MchApplyment mchApplyment) {

        // 获取到 服务商配置信息
        WxpayIsvParams wxpayIsvParams = (WxpayIsvParams)configContextQueryService.queryIsvParams(mchApplyment.getIsvNo(), CS.IF_CODE.WXPAY);

        // 特约商户进件相关服务
        Applyment4SubService applyment4SubService = getApplyment4SubService(wxpayIsvParams);

        // 获取详细参数
        WxpayApplymentInfo info = JSON.parseObject(mchApplyment.getApplyDetailInfo(), WxpayApplymentInfo.class);

        MchApplyment result = new MchApplyment();
        result.setApplyDetailInfo(JSON.toJSONString(info)); //重新更新全量参数（包含imgCache）

        try {
            // 超级管理员信息
            WxPayApplyment4SubCreateRequest.ContactInfo contactInfo = WxPayApplyment4SubCreateRequest.ContactInfo.builder()
                    .contactType(info.getContactType()) // 超级管理员类型 LEGAL：经营者/法人 SUPER：经办人
                    .contactName(info.getIdcardName()) // 超级管理员姓名
                    .contactEmail(info.getContactEmail()) // 联系邮箱
                    .mobilePhone(info.getContactPhone()) // 联系手机
                    .build();

            // 超管为 经办人
            if ("SUPER".equals(info.getContactType())) {
                contactInfo.setContactIdDocType("IDENTIFICATION_TYPE_IDCARD"); // 默认身份证
                contactInfo.setContactIdDocCopy(this.uploadImg(info.getContactIdDocCopy(), wxpayIsvParams, info)); // 超管身份证人像照
                contactInfo.setContactIdDocCopyBack(this.uploadImg(info.getContactIdDocCopyBack(), wxpayIsvParams, info)); // 超管身份证国徽照
                contactInfo.setContactName(info.getContactName()); // 超级管理员姓名
                contactInfo.setContactIdNumber(info.getContactIdNumber()); // 超级管理员身份证件号码
                contactInfo.setContactPeriodBegin(info.getContactIdDocPeriodBegin()); // 超管身份证起始有效期
                contactInfo.setContactPeriodEnd(info.getContactIdDocPeriodEnd()); // 超管身份证结束有效期
                contactInfo.setBusinessAuthorizationLetter(this.uploadImg(info.getBusinessAuthorizationLetter(), wxpayIsvParams, info)); // 业务办理授权函
            }

            // 主体资料
            // 法人身份证信息
            WxPayApplyment4SubCreateRequest.SubjectInfo.IdentityInfo.IdCardInfo idCardInfo = WxPayApplyment4SubCreateRequest.SubjectInfo.IdentityInfo.IdCardInfo.builder()
                    .idCardCopy(this.uploadImg(info.getIdcard1Img(), wxpayIsvParams, info)) // 法人身份证人像面照片
                    .idCardNational(this.uploadImg(info.getIdcard2Img(), wxpayIsvParams, info)) // 法人身份证国徽面照片
                    .idCardName(info.getIdcardName()) // 法人身份证姓名
                    .idCardNumber(info.getIdcardNo()) // 法人身份证号
                    .cardPeriodBegin(info.getIdcardEffectBegin()) // 法人身份证起始有效期，格式为yyyy-MM-dd
                    .cardPeriodEnd(info.getIdcardEffectEnd()) // 法人身份证结束有效期，格式为yyyy-MM-dd，有效期为长期，请填写：长期。
                    .build();
            // 主体为企业时
            if (MchApplyment.MERCHANT_TYPE_ENTERPRISE == info.getMerchantType()) {
                idCardInfo.setIdCardAddress(info.getIdcardAddress()); // 身份证居住地址
            }

            // 经营者/法人证件信息
            WxPayApplyment4SubCreateRequest.SubjectInfo.IdentityInfo identityInfo = WxPayApplyment4SubCreateRequest.SubjectInfo.IdentityInfo.builder() // 法人信息
                    .idDocType(IdTypeEnum.IDENTIFICATION_TYPE_IDCARD) // 证件类型：身份证
                    .idCardInfo(idCardInfo) // 经营者/法人信息
                    .build();
            // 主体为企业时
            if (info.getMerchantType() == MchApplyment.MERCHANT_TYPE_ENTERPRISE) {
                identityInfo.setOwner(info.getOwner()); // 经营者/法人是最终受益人
            }

            WxPayApplyment4SubCreateRequest.SubjectInfo subjectInfo = WxPayApplyment4SubCreateRequest.SubjectInfo.builder()
                    .subjectType(convertMerchantType(info.getMerchantType())) // 主体类型
                    .businessLicenseInfo(WxPayApplyment4SubCreateRequest.SubjectInfo.BusinessLicenseInfo.builder()
                            .licenseNumber(info.getLicenseNo()) // 营业执照号
                            .licenseCopy(this.uploadImg(info.getLicenseImg(), wxpayIsvParams, info)) // 营业执照图片
                            .licenseAddress(info.getLicenseAddress()) // 注册地址
                            .periodBegin(info.getLicenseEffectBegin())
                            .periodEnd(info.getLicenseEffectEnd())
                            .merchantName(info.getMchFullName()) // 营业执照商户名称
                            .legalPerson(info.getIdcardName()) // 个体户经营者/法人姓名
                            .build())
                    .identityInfo(identityInfo)
                    .build();


            // 受益人信息(JSON数组字符串)，企业必传
            if(info.getMerchantType() == MchApplyment.MERCHANT_TYPE_ENTERPRISE && info.getCompanyBeneficiaryList() != null){
                List<WxPayApplyment4SubCreateRequest.SubjectInfo.UboInfo> uboInfoList = new LinkedList<>();

                for (IdcardInfo idcardInfo : info.getCompanyBeneficiaryList()) {
                    WxPayApplyment4SubCreateRequest.SubjectInfo.UboInfo uboInfo = new WxPayApplyment4SubCreateRequest.SubjectInfo.UboInfo();

                    uboInfo.setUboIdDocType(IdTypeEnum.IDENTIFICATION_TYPE_IDCARD);
                    uboInfo.setUboIdDocCopy(this.uploadImg(idcardInfo.getIdcard1Img(), wxpayIsvParams, info));
                    uboInfo.setUboIdDocCopyBack(this.uploadImg(idcardInfo.getIdcard2Img(), wxpayIsvParams, info));
                    uboInfo.setUboIdDocName(idcardInfo.getIdcardName());
                    uboInfo.setUboIdDocNumber(idcardInfo.getIdcardNo());
                    uboInfo.setUboIdDocAddress(idcardInfo.getIdcardAddress());
                    uboInfo.setUboPeriodBegin(idcardInfo.getIdcardEffectBegin());
                    uboInfo.setUboPeriodEnd(idcardInfo.getIdcardEffectEnd());

                    uboInfoList.add(uboInfo);
                }

                subjectInfo.setUboInfoList(uboInfoList);
            }

            // 经营资料
            WxPayApplyment4SubCreateRequest.BusinessInfo businessInfo = WxPayApplyment4SubCreateRequest.BusinessInfo.builder()
                    .merchantShortname(info.getMchShortName()) // 商户简称
                    .servicePhone(info.getServicePhone()) // 客服电话
                    .salesInfo(WxPayApplyment4SubCreateRequest.BusinessInfo.SalesInfo.builder()
                            .salesScenesType(Collections.singletonList(SalesScenesTypeEnum.SALES_SCENES_STORE)) // 经营场景类型  SALES_SCENES_STORE：线下门店
                            .bizStoreInfo(WxPayApplyment4SubCreateRequest.BusinessInfo.SalesInfo.BizStoreInfo.builder()
                                    .bizStoreName(info.getStoreName()) // 经营店铺名称，签约收单产品必填
                                    .bizAddressCode(info.getAreaCode().getString(2)) // 门店省市编码
                                    .bizStoreAddress(info.getAddress()) // 门店详细地址
                                    .storeEntrancePic(Collections.singletonList(this.uploadImg(info.getStoreOuterImg(), wxpayIsvParams, info))) // 门店门头照片
                                    .indoorPic(Collections.singletonList(this.uploadImg(info.getStoreInnerImg(), wxpayIsvParams, info))) // 店内环境照片
                                    .build())
                            .build())
                    .build();

            // 结算规则
            WxPayApplyment4SubCreateRequest.SettlementInfo settlementInfo = WxPayApplyment4SubCreateRequest.SettlementInfo.builder()
                    .settlementId(info.getSettlementId()) // 入驻结算规则ID，参考：https://pay.weixin.qq.com/wiki/doc/apiv3_partner/terms_definition/chapter1_1_3.shtml#part-7
                    .qualificationType(info.getMccCode())
                    .activitiesId(info.getActivitiesId())
                    .activitiesRate(info.convertPaywayFeeList2String())
                    .build();
            if (StringUtils.isNotBlank(info.getQualifications())) {
                settlementInfo.setQualifications(Arrays.asList(this.uploadImg(info.getQualifications(), wxpayIsvParams, info)));
            }


            // 结算银行账户
            String bankAddressCode = null;
            if(info.getSettAccountBankBranchAreaCode() != null && info.getSettAccountBankBranchAreaCode().size() > 1){
                bankAddressCode = info.getSettAccountBankBranchAreaCode().getString(2);
            }
            WxPayApplyment4SubCreateRequest.BankAccountInfo bankAccountInfo = WxPayApplyment4SubCreateRequest.BankAccountInfo.builder()
                    .bankAccountType(convertSettAccountType(info.getSettAccountType())) // 账户类型
                    .accountName(info.getSettAccountName()) // 开户名称
                    .accountNumber(info.getSettAccountNo())  // 银行账号
                    .accountBank(info.getSettAccountBankName()) // 开户银行
                    .bankAddressCode(bankAddressCode) // 开户银行省市编码
                    .build();

            if ("其他银行".equals(info.getSettAccountBankName())) {
                bankAccountInfo.setBankName(info.getSettAccountBankBranchName()); // 开户银行支行名称
            }

            // 进件请求
            WxPayApplyment4SubCreateRequest request = WxPayApplyment4SubCreateRequest.builder()
                    .businessCode(mchApplyment.getApplyId())
                    .contactInfo(contactInfo)
                    .subjectInfo(subjectInfo)
                    .businessInfo(businessInfo)
                    .settlementInfo(settlementInfo)
                    .bankAccountInfo(bankAccountInfo)
                    .build();


            log.info("请求微信进件接口");
            WxPayApplymentCreateResult createResult = applyment4SubService.createApply(request);

            if (createResult != null) {
                result.setState(MchApplyment.STATE_AUDITING);
                result.setChannelApplyNo(createResult.getApplymentId()); // 放置请求ID
            } else {
                result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
            }
            return result;
        } catch (WxPayException e) {
            log.error("微信进件失败", e);

            result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
            result.setApplyErrorInfo(e.getErrCode() + "[" + e.getCustomErrorMsg() + "]");
            return result;
        } catch (IOException e) {
            log.error("微信进件失败", e);

            result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
            result.setApplyErrorInfo("[" + e.getMessage() + "]");
            return result;
        }
    }


    @Override
    public MchApplyment rejectModify(MchApplyment mchApplyment) {
        return firstApplyment(mchApplyment);
    }

    @Override
    public MchApplyment replenishInfo(MchApplyment mchApplyment) {
        return null;
    }

    @Override
    public MchApplyment query(MchApplyment mchApplyment) {

        // 获取到 服务商配置信息
        ConfigContextQueryService configContextQueryService = SpringBeansUtil.getBean(ConfigContextQueryService.class);
        WxpayIsvParams wxpayIsvParams = (WxpayIsvParams)configContextQueryService.queryIsvParams(mchApplyment.getIsvNo(), CS.IF_CODE.WXPAY);

        // 特约商户进件相关服务
        Applyment4SubService applyment4SubService = getApplyment4SubService(wxpayIsvParams);

        // 返回
        MchApplyment result = new MchApplyment();

        try {
            ApplymentStateQueryResult queryResult = applyment4SubService.queryApplyStatusByBusinessCode(mchApplyment.getApplyId());

            // 放置请求ID
            result.setChannelApplyNo(queryResult.getApplymentId());
            result.setState(mchApplyment.getState());

            switch (queryResult.getApplymentState()) {
                case APPLYMENT_STATE_EDITTING:    // 编辑中，提交申请发生错误导致，请尝试重新提交。
                case APPLYMENT_STATE_REJECTED:    // 已驳回，请按照驳回原因修改申请资料。
                case APPLYMENT_STATE_CANCELED:    // 已作废，申请单已被撤销。
                    result.setState(MchApplyment.STATE_REJECT_WAIT_MODIFY);
                    result.setApplyErrorInfo(queryResult.getApplymentStateMsg() + "【" + queryResult.getAuditDetail() + "】");
                    break;
                case APPLYMENT_STATE_AUDITING:    // 审核中
                    result.setState(MchApplyment.STATE_AUDITING);
                    break;
                case APPLYMENT_STATE_TO_BE_CONFIRMED:    // 待账户验证，请超级管理员使用微信打开返回的“签约链接”，根据页面指引完成账户验证。
                    result.setState(MchApplyment.STATE_WAIT_VERIFY);
                    result.setApplyErrorInfo(queryResult.getSignUrl());
                    break;
                case APPLYMENT_STATE_TO_BE_SIGNED:    // 待签约，请超级管理员使用微信打开返回的“签约链接”，根据页面指引完成签约。
                    result.setState(MchApplyment.STATE_WAIT_SIGN);
                    result.setApplyErrorInfo(queryResult.getSignUrl());
                    break;
                case APPLYMENT_STATE_SIGNING:    // 开通权限中，系统开通相关权限中，请耐心等待。
                    result.setState(mchApplyment.getState());
                    break;
                case APPLYMENT_STATE_FINISHED:    // 已完成，商户入驻申请已完成。
                    result.setState(MchApplyment.STATE_SUCCESS);
                    // 封装支付参数
                    WxpayIsvsubMchParams params = new WxpayIsvsubMchParams();
                    params.setSubMchId(queryResult.getSubMchid());
                    result.setSuccResParameter(JSON.toJSONString(params));
            }
        } catch (WxPayException e) {
            log.error("微信进件失败", e);

            result.setApplyErrorInfo(e.getErrCode() + "[" + e.getCustomErrorMsg() + "]");
        }

        return result;
    }

    @Override
    public ApplymentSignInfo signInfo(MchApplyment mchApplyment) {
        return null;
    }


    /** 上传照片， 返回mediaId  **/
    private String uploadImg(String img, WxpayIsvParams wxpayIsvParams, WxpayApplymentInfo info) throws IOException, WxPayException {

        if(StringUtils.isEmpty(img)){
            return null;
        }

        if(info.getUploadImgCache() != null && StringUtils.isNotEmpty(info.getUploadImgCache().get(img))){
            return info.getUploadImgCache().get(img);
        }

        // 微信上传服务
        MerchantMediaService mediaService = getMerchantMediaService(wxpayIsvParams);

        // 请求地址
        ImageUploadResult uploadResult = mediaService.imageUploadV3(new ByteArrayInputStream(HttpUtil.downloadBytes(img)), img);
        log.info("微信上传图片响应数据：{}", uploadResult.toString());

        String mediaId = uploadResult.getMediaId();

        if(info.getUploadImgCache() == null){
            info.setUploadImgCache(new HashMap<>());
        }
        info.getUploadImgCache().put(img, mediaId);
        return mediaId;
    }

    /**获取 wxJava 特约商户进件service */
    private Applyment4SubService getApplyment4SubService(WxpayIsvParams wxpayIsvParams) {
        return new Applyment4SubServiceImpl(getWxPayService(wxpayIsvParams));
    }

    /** 获取 wxJava 媒体文件上传service */
    private MerchantMediaService getMerchantMediaService(WxpayIsvParams wxpayIsvParams) {
        return new MerchantMediaServiceImpl(getWxPayService(wxpayIsvParams));
    }

    /** 获取 wxJava WxPayService */
    private WxPayService getWxPayService(WxpayIsvParams wxpayIsvParams) {
        return WxServiceWrapper.buildWxServiceWrapper(wxpayIsvParams, null).getWxPayService();
    }

    /** 主体类型转换：标准格式 ---》 接口自定义格式 **/
    public SubjectTypeEnum convertMerchantType(Byte merchantType){

        if(merchantType == null){
            return null;
        }

        if(merchantType == MchApplyment.MERCHANT_TYPE_INDIVIDUAL){
            return SubjectTypeEnum.SUBJECT_TYPE_INDIVIDUAL;
        }else if(merchantType == MchApplyment.MERCHANT_TYPE_ENTERPRISE){
            return SubjectTypeEnum.SUBJECT_TYPE_ENTERPRISE;
        }else if(merchantType == MchApplyment.MERCHANT_TYPE_GOV){
            return SubjectTypeEnum.SUBJECT_TYPE_INSTITUTIONS;
        }else if(merchantType == MchApplyment.MERCHANT_TYPE_OTHER){
            return SubjectTypeEnum.SUBJECT_TYPE_OTHERS;
        }
        return null;
    }

    /** 结算账号类型转换：标准格式 ---》 接口自定义格式 **/
    public BankAccountTypeEnum convertSettAccountType(String settAccountType){

        if(StringUtils.isEmpty(settAccountType)){
            return null;
        }

        if(settAccountType.equals(MchApplyment.SETT_ACCOUNT_TYPE_CORPORATE)){
            return BankAccountTypeEnum.BANK_ACCOUNT_TYPE_CORPORATE;
        }else if(settAccountType.equals(MchApplyment.SETT_ACCOUNT_TYPE_PERSONAL)){
            return BankAccountTypeEnum.BANK_ACCOUNT_TYPE_PERSONAL;
        }
        return null;
    }

}
