package com.ywt.alipaympapi.core.utils;

import com.alipay.easysdk.factory.Factory;
import com.alipay.easysdk.kernel.Config;
import com.ywt.alipaympapi.core.ConstantDef;
import com.ywt.alipaympapi.models.Constants;
import com.ywt.alipaympapi.models.auth.AlipayMpCfg;
import com.ywt.alipaympapi.models.checkIn.CheckInConstants;
import com.ywt.alipaympapi.models.enums.TerminalEnum;
import com.ywt.alipaympapi.models.union_reg.HospitalCustomInfo;
import com.ywt.alipaympapi.web.interceptors.WebAppContext;
import com.ywt.core.exception.AppMessageException;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.regex.Pattern;

public final class BizUtil {


    /**
     * 根据当前终端获取所在医院 id
     *
     * @return 医院 id
     */
    public static int getCurrentHospitalId() {
        switch (TerminalEnum.valueOf(ContextHelper.getCurrentTerminalWrapped())) {
            case NFYYBYFY_ALIPAY_MP:
            case NFYYBYFY_WXAPP:
                return Constants.HOSPITAL_ID_NFYYBYFY;
            case TH_ALIPAY_MP:
                return Constants.TAIHE_HOSPITAL_ID;
            default:
                return 0;
        }
    }


    /**
     * 字符串脱敏，指定保留前后 resBit 位明文显示，剩余字符串以`*`号显示。若字符串长度小于等于 2*resBit，则全部明文显示
     *
     * @param s 待处理字符串
     * @return 处理后的字符串
     */
    public static String masked(String s, int resBit) {
        if (s == null) return "";
        if (s.length() <= 2 * resBit) return s;
        int maskedBitLen = s.length() - 2 * resBit;
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < maskedBitLen; i++) {
            sb.append("*");
        }
        return s.substring(0, resBit) + sb.toString() + s.substring(s.length() - resBit);
    }

    public static String masked(String s) {
        return masked(s, 3);
    }

    public static HospitalCustomInfo getHospitalCustomInfo(int hospitalId) {
        HospitalCustomInfo def = genHospitalCustomInfo(0, "", "", "");
        return getHospitalCustomInfoList().stream().filter(i -> hospitalId == i.getHospitalId()).findFirst().orElse(def);
    }

    /**
     * 获取对应的患者客户端terminal值
     * <p>
     * 根据当前客户端，判断是否是医生版，若是则返回与该医生版对应的患者客户端terminal值，否则返回0
     *
     * @param currentTerminal 当前客户端terminal值
     * @return 对应的患者客户端terminal值, 0: 找不到对应值
     */
    public static int getAssociatedPatientTerminal(int currentTerminal) {
        if (!TerminalEnum.isDoctorTerminal(currentTerminal)) return 0;
        switch (TerminalEnum.valueOf(currentTerminal)) {
            case DoctorWxOfficial:
                return TerminalEnum.PatientWxOfficial.getValue();
            case TAI_HE_INTERNET_HOSPITAL_DOCTOR_WX_OFFICIAL:
                return TerminalEnum.TAI_HE_INTERNET_HOSPITAL_PATIENT_WX_OFFICIAL.getValue();
            default:
                return 0;
        }
    }

    /**
     * convert cent to yuan
     *
     * @param cent cent
     * @return value of yuan
     */
    public static double cent2Yuan(int cent) {
        BigDecimal v1 = new BigDecimal(String.valueOf(cent));
        BigDecimal v2 = new BigDecimal("100");
        BigDecimal result = v1.divide(v2, 2, RoundingMode.HALF_UP);
        return result.doubleValue();
    }

    /**
     * convert yuan to cent
     *
     * @param yuan yuan
     * @return value of cent
     */
    public static double yuan2Cent(double yuan) {
        BigDecimal v1 = new BigDecimal(String.valueOf(yuan));
        BigDecimal v2 = new BigDecimal("100");
        BigDecimal result = v1.multiply(v2);
        return result.intValue();
    }

    public static int yuan2Cent(String yuan) throws NumberFormatException {
        BigDecimal v1 = new BigDecimal(yuan);
        BigDecimal v2 = new BigDecimal("100");
        BigDecimal result = v1.multiply(v2);
        return result.intValue();
    }

    public static boolean containsEmoji(String source) {
        return Pattern.compile("[\\ud800\\udc00-\\udbff\\udfff\\ud800-\\udfff]").matcher(source).find();
    }

    public static boolean endsWithIgnoreCase(String str, String suffix) {
        int suffixLength = suffix.length();
        return str.regionMatches(true, str.length() - suffixLength, suffix, 0, suffixLength);
    }

    /**
     * 根据终端号返回对应的医院 id，目前支持太和公众号、南方医院白云分院小程序
     * <p/>
     * TODO: {@link TerminalEnum#DOCTOR_MINI_PROGRAM}, {@link TerminalEnum#PRESCRIPTION_SYSTEM_PC} 的处理？根据医生进行业务的医院决定
     *
     * @param terminalId 终端号
     * @return 医院 id
     */
    public static int getHospitalIdByTerminalId(int terminalId) {
        switch (TerminalEnum.valueOf(terminalId)) {
            case NFYYBYFY_WXAPP:
            case NFYYBYFY_DOC_WX_OFFICIAL:
            case NFYYBYFY_OFFICIAL:
                return ConstantDef.NFYYBYFY_HOSPITAL_ID;
            case TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM:
            case TAI_HE_INTERNET_HOSPITAL_PATIENT_WX_OFFICIAL:
            case TAI_HE_INTERNET_HOSPITAL_DOCTOR_WX_OFFICIAL:
            case TaiheWxOfficial:
            case DOCTOR_MINI_PROGRAM:
            case TH_ALIPAY_MP:
                return ConstantDef.TAIHE_HOSPITAL_ID;
            default:
                return 0;
        }
    }

    /**
     * 根据医院 id 获取对应支付宝小程序终端号
     */
    public static int getAlipayMpTerminalIdByHospitalId(int hospitalId) {
        switch (hospitalId) {
            case Constants.HOSPITAL_ID_NFYYBYFY:
                return TerminalEnum.NFYYBYFY_ALIPAY_MP.getValue();
            case Constants.TAIHE_HOSPITAL_ID:
                return TerminalEnum.TH_ALIPAY_MP.getValue();
            default:
                return TerminalEnum.UNKNOWN.getValue();
        }
    }

    public static int getCurrentTerminalWrapped() {
//        return TerminalEnum.NFYYBYFY_WXAPP.getValue();
//        return TerminalEnum.TaiheWxOfficial.getValue();
        return WebAppContext.current().getTerminal();
    }

    /**
     * 根据医院 id 获取对应的互联网科室 id，目前只支持太和分院
     *
     * @param hospitalId 医院 id
     * @return 对应的互联网科室代号
     */
    public static String getNetDeptCodeByHospitalId(int hospitalId) {
        switch (hospitalId) {
            case ConstantDef.TAIHE_HOSPITAL_ID:
                return ConstantDef.TAIHE_NET_DEPT_CODE;
            case ConstantDef.NFYYBYFY_HOSPITAL_ID:
                return ConstantDef.NFYYBYFY_NET_DEPT_CODE;
            default:
                return "";
        }
    }


    /**
     * 根据 hospitalId 返回 hospital name, 不查询数据库，直接读取 {@link ConstantDef}
     *
     * @param hospitalId 医院 id
     * @return 医院名称
     */
    public static String getHospitalNameByIdSimply(int hospitalId) {
        switch (hospitalId) {
            case ConstantDef.TAIHE_HOSPITAL_ID:
                return ConstantDef.TAIHE_HOSPITAL_NAME;
            case ConstantDef.NFYYBYFY_HOSPITAL_ID:
                return ConstantDef.NFYYBYFY_HOSPITAL_NAME;
            default:
                return "";
        }
    }

    /**
     * 根据医院 id 获取对应的互联网科室 id，目前只支持太和分院
     *
     * @param hospitalId 医院 id
     * @return 对应的互联网科室代号
     */
    public static List getNetDeptCodeListByHospitalId(int hospitalId) {
        List<String> list = new ArrayList<>();
        switch (hospitalId) {
            case ConstantDef.TAIHE_HOSPITAL_ID:
                list.add(ConstantDef.TAIHE_NET_DEPT_CODE);
                list.add(ConstantDef.TAIHE_NET_DEPT_CODE_NIGHT_DIAGNOSIS);
                return list;
            case ConstantDef.NFYYBYFY_HOSPITAL_ID:
                list.add(ConstantDef.TAIHE_NET_DEPT_CODE);
                return list;
            default:
                return list;
        }
    }

    public static List<Integer> getDefaultBizSupportedHospitalIds() {
        return Arrays.asList(
                ConstantDef.TAIHE_HOSPITAL_ID,
                CheckInConstants.NFYYBYFY_HS_SUB_HOSPITAL_ID,
                CheckInConstants.NFYYBYFY_SH_SUB_HOSPITAL_ID
        );
    }


    /**
     * 返回医院的别名和所在地区名称、地址等
     * 这些名称一般不会变动，所以直接 hard code。如果后续经常修改，可以维护进 hospital 表
     */
    public static List<HospitalCustomInfo> getHospitalCustomInfoList() {
        HospitalCustomInfo i1 = genHospitalCustomInfo(CheckInConstants.NFYYBYFY_SH_SUB_HOSPITAL_ID, "广州市天河区广州大道中1305号", "沙河院区", "广州天河区");
        HospitalCustomInfo i2 = genHospitalCustomInfo(CheckInConstants.NFYYBYFY_HS_SUB_HOSPITAL_ID, "广州市白云区黄石街元下底路23号", "黄石院区", "广州白云区");
        HospitalCustomInfo i3 = genHospitalCustomInfo(ConstantDef.TAIHE_HOSPITAL_ID, "广州市白云区太和中路53号", "太和分院", "广州白云区");
        return Arrays.asList(i1, i2, i3);
    }

    public static HospitalCustomInfo genHospitalCustomInfo(int hospitalId, String addr, String alias, String area) {
        HospitalCustomInfo i = new HospitalCustomInfo();
        i.setHospitalId(hospitalId);
        i.setAddr(addr);
        i.setAlias(alias);
        i.setAreaName(area);
        return i;
    }

    /**
     * 根据终端获取不同小程序的配置
     * TODO: 后续多医院支持需要改成配置化
     */
    public static AlipayMpCfg getCfg(int terminal) throws AppMessageException {
        if (TerminalEnum.valueOf(terminal) == TerminalEnum.NFYYBYFY_ALIPAY_MP) {
            return getCfg4Nfyybyfy();
        }
        if (TerminalEnum.valueOf(terminal) == TerminalEnum.TH_ALIPAY_MP) {
            return getCfg4Nfth();
        }
        throw new AppMessageException(String.format("Unsupported terminal %d", terminal));
    }

    // 获取所有支持的小程序配置
    public static List<AlipayMpCfg> getAllCfgs() {
        List<AlipayMpCfg> list = new ArrayList<>();
        list.add(getCfg4Nfyybyfy());
        list.add(getCfg4Nfth());
        return list;
    }

    public static AlipayMpCfg getAlipayMpCfgByAppId(String appId) throws AppMessageException {
        List<AlipayMpCfg> cfgs = BizUtil.getAllCfgs();
        AlipayMpCfg cfg = cfgs.stream().filter(i -> Checker.getStringValue(i.getAppId()).equals(appId)).findFirst().orElse(null);
        if (cfg == null) throw new AppMessageException("配置有误，找不到 appId " + appId);
        return cfg;
    }

    // 白云分院小程序配置数据
    private static AlipayMpCfg getCfg4Nfyybyfy() {
        AlipayMpCfg cfg = new AlipayMpCfg();
        cfg.setAppId(Constants.ALIPAYMP_APPID_NFYYBYFY);
        cfg.setSaasSecurityKey(Constants.ALIPAYMP_SAAS_SEC_KEY_NFYYBYFY);
        cfg.setIsvPid(Constants.ALIPAYMP_ISV_PID_NFYYBYFY);
        cfg.setMerchantPrivateKey(Constants.MERCHANT_PRIVATE_KEY_NFYYBYFY);
        cfg.setPublicKey(Constants.ALIPAY_PUBLIC_KEY_NFYYBYFY);
        cfg.setHospitalId(Constants.HOSPITAL_ID_NFYYBYFY);
        cfg.setTerminal(TerminalEnum.NFYYBYFY_ALIPAY_MP.getValue());
        return cfg;
    }

    // 太和分院小程序配置数据
    private static AlipayMpCfg getCfg4Nfth() {
        AlipayMpCfg cfg = new AlipayMpCfg();
        cfg.setAppId(Constants.ALIPAYMP_APPID_NFTH);
        cfg.setSaasSecurityKey(Constants.ALIPAYMP_SAAS_SEC_KEY_NFTH);
        cfg.setIsvPid(Constants.ALIPAYMP_ISV_PID_NFTH);
        cfg.setMerchantPrivateKey(Constants.MERCHANT_PRIVATE_KEY_NFTH);
        cfg.setPublicKey(Constants.ALIPAY_PUBLIC_KEY_NFTH);
        cfg.setHospitalId(Constants.TAIHE_HOSPITAL_ID);
        cfg.setTerminal(TerminalEnum.TH_ALIPAY_MP.getValue());
        return cfg;
    }


    /**
     * 初始化 alipay-easy-sdk
     * TODO: 后续扩展多医院需要将 pk & apk 配置化
     * 为了节省服务器资源不分医院单独部署，每次调用 alipay-easy sdk 前需要初始化，目前看来性能没有影响
     */
    public static void initAlipayEasySdk(int terminal) throws AppMessageException {
        Config config = new Config();
        config.protocol = "https";
        config.gatewayHost = "openapi.alipay.com";
        config.signType = "RSA2";

        AlipayMpCfg cfg = BizUtil.getCfg(terminal);
        config.appId = Checker.getStringValue(cfg.getAppId());
        // 为避免私钥随源码泄露，推荐从文件中读取私钥字符串而不是写入源码中
        config.merchantPrivateKey = Checker.getStringValue(cfg.getMerchantPrivateKey());
        // 注：如果采用非证书模式，则无需赋值上面的三个证书路径，改为赋值如下的支付宝公钥字符串即可
        config.alipayPublicKey = Checker.getStringValue(cfg.getPublicKey());
        Factory.setOptions(config);
    }
}
