package com.zd.base.message.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.zd.base.message.domain.UserOpenId;
import com.zd.base.message.properties.WeChatProperties;
import com.zd.base.message.service.IUserOpenIdService;
import com.zd.base.message.service.IWechatMsgSendService;
import com.zd.common.core.exception.ServiceException;
import com.zd.common.core.template.Template;
import com.zd.common.core.template.TemplateParam;
import com.zd.common.core.template.TemplateResult;
import com.zd.common.core.template.WxUserInfo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundValueOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 向微信小程序推送私密消息
 */
@Slf4j
@Service
public class WechatMsgSendServiceImpl implements IWechatMsgSendService {

    @Resource(name = "redisTemplate")
    protected RedisTemplate<String, String> redisTemplate;
    @Autowired
    private WeChatProperties weChatProperties;

    @Resource
    private IUserOpenIdService openIdService;

    private final SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    /**
     * 微信获取openId的返回值
     */
    private static final Map<Integer, String> messageOpenIdMap = new HashMap<>();

    static {
        messageOpenIdMap.put(-1, "系统繁忙，此时请开发者稍候再试");
        messageOpenIdMap.put(0, "请求成功");
        messageOpenIdMap.put(40029, "code 无效");
        messageOpenIdMap.put(45011, "频率限制，每个用户每分钟100次");
        messageOpenIdMap.put(40226, "高风险等级用户，小程序登录拦截 。风险等级详见用户安全解方案");
    }

    /**
     * 微信发送模板消息的返回值
     */
    private static final Map<Integer, String> sendMessageResultMap = new HashMap<>();

    static {
        sendMessageResultMap.put(40003, "openid 为空或者不正确");
        sendMessageResultMap.put(40037, "订阅模板 id 为空不正确");
        sendMessageResultMap.put(43101, "用户拒绝接受消息，如果用户之前曾经订阅过，则表示用户取消了订阅关系");
        sendMessageResultMap.put(47003, "模板参数不准确，可能为空或者不满足规则");//errmsg会提示具体是哪个字段出错
        sendMessageResultMap.put(41030, "page路径不正确，需要保证在现网版本小程序中存在，与 app.json 保持一致");
    }

    /**
     * 资格申请、用气申请通知发送(跳转页面：资格申请-资格详情、用气申请-申请详情)
     *
     * @param userId      用户ID
     * @param checkType   审核类型 1：资格申请 2：用气申请
     * @param checkStatus 审核状态
     * @param checkTime   审核时间
     * @param taskId      任务ID
     * @return TemplateResult 通知发送结果
     */
    @Override
    public TemplateResult sendCheckResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId) {
        // checkType 双端页面不一致，判断跳转页面地址写入
        String page = null;
        if (checkType == 2) {
            page = weChatProperties.getAirCheckUrl();
        }
        if (checkType == 1) {
            page = weChatProperties.getQualificationCheckUrl();
        }
        return getTemplateResult(userId, checkType, checkStatus, checkTime, taskId, page);
    }

    private TemplateResult getTemplateResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId, String page) {
        page = page + "?id=" + taskId;
        List<TemplateParam> paras = new ArrayList<>();
        paras.add(new TemplateParam("thing39", checkType == 1 ? "资格申请" : "用气申请"));//申请事项
        paras.add(new TemplateParam("phrase5", checkStatus == 1 ? "成功" : "失败"));//审核结果
        paras.add(new TemplateParam("time24", dateFormat.format(checkTime)));//审核时间
        return sendMessage(userId, weChatProperties.getCheckTemplateId(), page, paras);
    }

    /**
     * 待办事项通知发送
     *
     * @param userId      用户ID
     * @param backlogName 待办事项名称
     * @param remark      备注
     * @return TemplateResult 通知发送结果
     */
    @Override
    public TemplateResult sendBacklogResult(Long userId, String backlogName, String remark) {
        Template template = new Template();
        String openId = getOpenId(userId);
        template.setToUser(openId);
        template.setTemplateId(weChatProperties.getBacklogTemplateId());
        // 供应商端-待办事项-需求明细
        template.setPage(weChatProperties.getBacklogUrl());

        List<TemplateParam> paras = new ArrayList<>();
        paras.add(new TemplateParam("thing3", backlogName));//待办事项
        paras.add(new TemplateParam("time5", dateFormat.format(Calendar.getInstance().getTime())));//通知时间
        paras.add(new TemplateParam("thing6", remark));//备注
        template.setData(paras);
        return sendMsgService(template);
    }

    @Override
    public List<String> getList() {
        return weChatProperties.getTemplateIds();
    }

    @Override
    public TemplateResult sendStuCheckResult(Long userId, Integer checkType, Integer checkStatus, Date checkTime, Long taskId) {
        // checkType 双端页面不一致，判断跳转页面地址写入
        String page = null;
        if (checkType == 2) {
            page = weChatProperties.getStuAirCheckUrl();
        }
        if (checkType == 1) {
            page = weChatProperties.getStuQualificationCheckUrl();
        }
        return getTemplateResult(userId, checkType, checkStatus, checkTime, taskId, page);
    }

    @Override
    public TemplateResult sendAlarm(List<Long> userIds, String address) {
        List<TemplateParam> paras = new ArrayList<>();
        paras.add(new TemplateParam("thing1", address));//报警位置
        paras.add(new TemplateParam("time3", dateFormat.format(Calendar.getInstance().getTime())));//上报时间
        paras.add(new TemplateParam("thing2", "气瓶超出范围报警"));//报警原因
        userIds.forEach(id -> sendMessage(id, weChatProperties.getDeviceAlarmTemplateId(), weChatProperties.getInitPage(), paras));
        return null;
    }

    private TemplateResult sendMessage(Long userId, String weChatProperties, String page, List<TemplateParam> paras) {
        Template template = getTemplate(userId, weChatProperties, page);
        if (template==null){
            return null;
        }
        template.setData(paras);
        return sendMsgService(template);
    }

    /**
     * 资格申请、用气申请管理端待审核通知发送
     *
     * @param userId    用户ID
     * @param checkType 审核类型 1：资格申请 2：用气申请
     * @param name      申请人
     * @param applyTime 申请时间
     * @return TemplateResult 通知发送结果
     */
    @Override
    public TemplateResult sendWaitCheckResult(Long userId, Integer checkType, String name, Date applyTime) {
        String page = weChatProperties.getWaitCheckUrl();
        List<TemplateParam> paras = new ArrayList<>();
        paras.add(new TemplateParam("name1", name));//申请事项
        paras.add(new TemplateParam("thing3", checkType == 1 ? "资格申请" : "用气申请"));//审核结果
        paras.add(new TemplateParam("time2", dateFormat.format(applyTime)));//审核时间
        return sendMessage(userId, weChatProperties.getWaitCheckTemplateId(), page, paras);
    }

    private Template getTemplate(Long userId, String templateId, String page) {
        Template template = new Template();
        String openId = getOpenId(userId);
        if (openId != null) {
            template.setToUser(openId);
            template.setTemplateId(templateId);
            template.setPage(page);
            return template;
        }
        return null;
    }

    /**
     * 出库确认通知
     *
     * @param userId  用户ID
     * @param outType 出库类型
     * @param outTime 出库时间
     * @return TemplateResult 通知发送结果
     */
    @Override
    public TemplateResult sendStorageOutResult(Long userId, String outType, Date outTime) {
        Template template = new Template();
        String openId = getOpenId(userId);
        template.setToUser(openId);
        template.setTemplateId(weChatProperties.getStorageOutTemplateId());
        List<TemplateParam> paras = new ArrayList<>();
        paras.add(new TemplateParam("thing2", outType));//出库类型
        paras.add(new TemplateParam("time9", dateFormat.format(outTime)));//出库时间
        template.setData(paras);
        return sendMsgService(template);
    }

    private String getOpenId(Long userId) {
        UserOpenId userOpenId = openIdService.getByUserId(userId);
        if (userOpenId == null) {
            return null;
        }
        return userOpenId.getOpenId();
    }

    @Override
    public String getOpenId(String code) {
        String openIdUrl = weChatProperties.getOpenIdUrl().replace("APPID", weChatProperties.getAppId())
                .replace("SECRET", weChatProperties.getSecret())
                .replace("JSCODE", code);
        String post = HttpUtil.get(openIdUrl);
        JSONObject jsonObject = JSON.parseObject(post);
        WxUserInfo wxUserInfo = JSON.toJavaObject(jsonObject, WxUserInfo.class);
        if (wxUserInfo.getErrcode() != 0) {
            String errMsg;
            int errCode = wxUserInfo.getErrcode();
            if (errCode == 40029) {
                return null;
            }
            if (messageOpenIdMap.containsKey(errCode)) {
                errMsg = messageOpenIdMap.get(errCode);
                throw new ServiceException(errMsg);
            }
        }
        return wxUserInfo.getOpenid();
    }

    private TemplateResult sendMsgService(Template template) {
        String url = weChatProperties.getUrl().replace("ACCESS_TOKEN", getAccessToken());
        String post = HttpUtil.post(url, template.toJSON());
        JSONObject jsonObject = JSON.parseObject(post);
        TemplateResult result = JSON.toJavaObject(jsonObject, TemplateResult.class);
        int errCode = result.getErrcode();
        if (errCode == 47003) {
            result.setErrmsg(sendMessageResultMap.get(errCode) + ":" + result.getErrmsg());
        } else if (sendMessageResultMap.containsKey(errCode)) {
            result.setErrmsg(sendMessageResultMap.get(errCode));
        }
        return result;
    }

    private String getAccessToken() {
        String accessToken = null;
        try {
            //查询是否还有缓存
            BoundValueOperations<String, String> ops = redisTemplate.boundValueOps("account_token");
            //缓存时长
            if (StringUtils.isBlank(ops.get())) {
                Map<String, Object> params = new HashMap<>();
                params.put("grant_type", "client_credential");
                params.put("appid", weChatProperties.getAppId());
                params.put("secret", weChatProperties.getSecret());
                String respData = HttpUtil.get(weChatProperties.getTokenUrl(), params);
                log.info("get access_token=====\n{}", respData);
                JSONObject json = JSON.parseObject(respData);
                ops.set(json.getString("access_token"));
                ops.expire(7100, TimeUnit.SECONDS);
            }
            accessToken = ops.get();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return accessToken;
    }
}
