package thyyxxk.webserver.service.medicalinsurance;

import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.binary.Base64;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import thyyxxk.webserver.config.envionment.MedinsurConfig;
import thyyxxk.webserver.config.exception.BizException;
import thyyxxk.webserver.constants.sidicts.SiFunction;
import thyyxxk.webserver.dao.his.medicalinsurance.SiZyDao;
import thyyxxk.webserver.utils.DateUtil;
import thyyxxk.webserver.utils.StringUtil;
import thyyxxk.webserver.utils.TokenUtil;

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * @description: 医保交易执行
 * @author: DingJie
 * @create: 2021-05-28 15:57:26
 **/
@Slf4j
@Service
public class ExecService {
    private static final SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
    private static final String RESULT_CODE = "infcode";
    private static final String OUTPUT = "output";
    private static int serial = 1000;
    private static final int MIN_VAL = 1000;
    private static final int MAX_VAL = 9999;
    private volatile String signNo;
    private final SiZyDao dao;
    private final RestTemplate template;
    private final MedinsurConfig cfg;

    @Autowired
    public ExecService(SiZyDao dao, RestTemplate template, MedinsurConfig cfg) {
        this.dao = dao;
        this.template = template;
        this.cfg = cfg;
    }

    public synchronized String makeMsgId() {
        serial += 1;
        if (serial > MAX_VAL) {
            serial = MIN_VAL;
        }
        return cfg.getHospId() + sdf.format(new Date()) + serial;
    }

    private synchronized String getSignNo() {
        if (StringUtil.isBlank(signNo)) {
            signIn();
        }
        return signNo;
    }

    public synchronized void signIn() {
        String dbSignNo = dao.getSignInNo();
        if (StrUtil.isNotBlank(dbSignNo)) {
            signNo = dbSignNo;
            log.info("获取历史签到号成功：{}", dbSignNo);
            return;
        }
        JSONObject input = makeSignHeader(SiFunction.SIGN_IN);
        JSONObject signIn = new JSONObject();
        signIn.put("opter_no", "99999");
        signIn.put("mac", cfg.getMacAddress());
        signIn.put("ip", cfg.getIpAddress());
        input.getJSONObject("input").put("signIn", signIn);
        JSONObject result = executeTrade(input, SiFunction.SIGN_IN);
        if (null != result && result.getInteger(RESULT_CODE) == 0) {
            try {
                JSONObject output = result.getJSONObject(OUTPUT);
                signNo = output.getJSONObject("signinoutb").getString("sign_no");
                dao.updateSignNo(signNo);
                log.info("签到成功，签到号：{}", signNo);
            } catch (Exception e) {
                throw new BizException();
            }
        }
    }

    public void signOut() {
        if (StringUtil.notBlank(signNo)) {
            JSONObject input = makeSignHeader(SiFunction.SIGN_OUT);
            JSONObject signOut = new JSONObject();
            signOut.put("sign_no", signNo);
            signOut.put("opter_no", "99999");
            input.getJSONObject("input").put("signOut", signOut);
            JSONObject result = executeTrade(input, SiFunction.SIGN_OUT);
            if (null != result && result.getIntValue(RESULT_CODE) == 0) {
                log.info("医保签退成功。");
                signNo = null;
                dao.updateSignNo(signNo);
            }
        }
    }

    public String getInstitutionArea(String insureArea) {
//        String hospArea = cfg.getHospArea();
//        String prefix = hospArea.substring(0, 4);
//        return insureArea.startsWith(prefix) ? hospArea : prefix.substring(0, 2) + "9900";
        return cfg.getHospArea();
    }

    private JSONObject makeSignHeader(SiFunction function) {
        JSONObject header = makePublicHeader("");
        header.put("infno", function.getCode());
        header.put("opter", "99999");
        header.put("opter_name", "全院");
        return header;
    }

    public JSONObject makeTradeHeader(SiFunction function) {
        String staffId = TokenUtil.getInstance().getTokenUserId();
        String staffName = dao.selectStaffName(staffId);
        JSONObject header = makePublicHeader(cfg.getHospArea());
        header.put("infno", function.getCode());
        header.put("opter", staffId);
        header.put("opter_name", staffName);
        header.replace("sign_no", getSignNo());
        return header;
    }

    public JSONObject makeTradeHeaderWithInsureArea(SiFunction function, String insureArea) {
        String staffId = TokenUtil.getInstance().getTokenUserId();
        String staffName = dao.selectStaffName(staffId);
        JSONObject header = makePublicHeader(insureArea);
        header.put("infno", function.getCode());
        header.put("opter", staffId);
        header.put("opter_name", staffName);
        header.replace("sign_no", getSignNo());
        return header;
    }

    private JSONObject makePublicHeader(String insureArea) {
        if (null == insureArea) {
            insureArea = "";
        }
        JSONObject header = new JSONObject();
        JSONObject input = new JSONObject();
        header.put("msgid", makeMsgId());
        header.put("mdtrtarea_admvs", getInstitutionArea(insureArea));
        header.put("insuplc_admdvs", insureArea);
        header.put("recer_sys_code", "医院");
        header.put("dev_no", "");
        header.put("dev_safe_info", "");
        header.put("cainfo", "");
        header.put("signtype", "");
        header.put("infver", cfg.getApiVersion());
        header.put("opter_type", "1");
        header.put("inf_time", DateUtil.now());
        header.put("fixmedins_code", cfg.getHospId());
        header.put("fixmedins_name", cfg.getHospName());
        header.put("sign_no", "");
        if (StrUtil.isNotBlank(cfg.getSoftDeveloper())) {
            header.put("fixmedins_soft_fcty", cfg.getSoftDeveloper());
        }
        header.put("input", input);
        return header;
    }

    public Map<String, String> getSiHeaderMap(SiFunction function) {
        Map<String, String> headers = new HashMap<>();
        String timestamp = String.valueOf(System.currentTimeMillis());
        String signature = getSignature(timestamp, function.getCode());
        // 医保原始地址添加header
        headers.put("_api_access_key", cfg.getAccessKey());
//        headers.put("secretKey", cfg.getSecretKey());
        headers.put("_api_name", cfg.getApiName());
        headers.put("_api_version", cfg.getApiVersion());
        headers.put("_api_timestamp", timestamp);
        headers.put("_api_signature", signature);
        // webhis.thyy.cn代理地址添加header
//        headers.put("apiAccessKey", cfg.getAccessKey());
//        headers.put("apiSecreKey", cfg.getSecretKey());
//        headers.put("apiName", StrUtil.isBlank(cfg.getApiName()) ? function.getCode() : cfg.getApiName());
//        headers.put("apiVersion", cfg.getApiVersion());
//        headers.put("apiTimestamp", timestamp);
//        headers.put("apiSignature", signature);

        return headers;
    }

    public HttpHeaders getHttpHeaders(SiFunction function) {
        String timestamp = String.valueOf(System.currentTimeMillis());
        String signature = getSignature(timestamp, function.getCode());
        HttpHeaders headers = new HttpHeaders();
//        headers.add("apiAccessKey", cfg.getAccessKey());
//        headers.add("apiSecreKey", cfg.getSecretKey());
//        headers.add("apiName",  StrUtil.isBlank(cfg.getApiName()) ? function.getCode() : cfg.getApiName());
//        headers.add("apiVersion", cfg.getApiVersion());
//        headers.add("apiTimestamp", timestamp);
//        headers.add("apiSignature", signature);

        headers.add("_api_access_key", cfg.getAccessKey());
//        headers.add("secretKey", cfg.getSecretKey());
        headers.add("_api_name", cfg.getApiName());
        headers.add("_api_version", cfg.getApiVersion());
        headers.add("_api_timestamp", timestamp);
        headers.add("_api_signature", signature);
        return headers;
    }

    public String getSignature(String timestamp, String function) {
        String source =
                "_api_access_key=" + cfg.getAccessKey() +
                        "&_api_name=" + cfg.getApiName() +
                        "&_api_timestamp=" + timestamp +
                        "&_api_version=" + cfg.getApiVersion();

//        _api_access_key=a94971b5ecee4bb994bce25c9291ccbf&_api_name=hssServives&_api_timestamp=1754062122524&_api_version=1.0.0
        return hmacSha1(source);
    }

//    public String getSignature() throws Exception {
//        String params=  "_api_access_key="+accessKey+"&_api_name="+apiservice+"&_api_timestamp="+timestamp+"&_api_version="+version;
//        String sign = (new BASE64Encoder()).encodeBuffer(HmacSHA1Encrypt(params,secretKey));
//    }
//    public static byte[] HmacSHA1Encrypt(String encryptText, String encryptKey) throws Exception {
//        byte[] data = encryptKey.getBytes(ENCODING);
//        SecretKey secretKey = new SecretKeySpec(data, MAC_NAME);
//        Mac mac = Mac.getInstance(MAC_NAME);
//        mac.init(secretKey);
//
//        byte[] text = encryptText.getBytes(ENCODING);
//        return mac.doFinal(text);
//    }

    private String hmacSha1(String src) {
        byte[] result = null;
        try {
            SecretKeySpec signInKey = new SecretKeySpec(cfg.getSecretKey().getBytes(), "HmacSHA1");
            Mac mac = Mac.getInstance("HmacSHA1");
            mac.init(signInKey);
            byte[] rawHmac = mac.doFinal(src.getBytes());
            result = Base64.encodeBase64(rawHmac);
        } catch (Exception e) {
            log.error("HmacSHA1加密出错", e);
        }
        return null == result ? null : new String(result);
    }

    public JSONObject executeTrade(JSONObject input, SiFunction function) {
        try {
            log.info("医保接口调用：【接口号：" + function.getCode() +
                    "，接口名：" + function.getName() +
                    "，操作员：" + input.getString("opter_name") + "】\r\n" +
                    "医保接口调用：参数：" + input);
            String result = template.postForObject(cfg.getApiUrl(),
                    new HttpEntity<>(input, getHttpHeaders(function)), String.class);
            log.info("医保接口调用：返回：" + result);

            return JSONObject.parseObject(result);
        } catch (Exception e) {
            log.error("医保网络出错", e);
            JSONObject object = new JSONObject();
            object.put("infcode", -1);
            if (e.getMessage().contains("Connection timed out")) {
                object.put("err_msg", "医保中心网络故障，连接超时。");
            } else {
                object.put("err_msg", e.getMessage());
            }
            return object;
        }
    }

    /**
     * 工伤接口URL
     */
    // 模拟接口地址（当前使用）
    private static final String WORK_INJURY_API_URL = "http://130.150.161.72:9206/thyy/api/public/injury/workinjury";
    // 真实接口地址（注释掉，需要时手动切换）
    // private static final String WORK_INJURY_API_URL = "http://localhost:8321/api/entry/workinjury";

    /**
     * 执行工伤接口调用（工伤接口使用不同的请求头格式）
     */
    public JSONObject executeWorkInjuryTrade(JSONObject input) {
        HttpHeaders headers = new HttpHeaders();
        headers.add("Content-Type", "application/json");
        // 工伤接口可能不需要医保接口的认证头，直接使用简单的JSON头
        return template.postForObject(WORK_INJURY_API_URL,
                new HttpEntity<>(input, headers), JSONObject.class);
    }
}
