package cn.iocoder.yudao.module.system.api.wechat;

import cn.hutool.core.lang.Assert;
import cn.iocoder.yudao.framework.common.enums.UserTypeEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.common.util.string.StringUtil;
import cn.iocoder.yudao.framework.common.util.time.TimeUtil;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.tenant.core.aop.TenantIgnore;
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
import cn.iocoder.yudao.module.system.api.wechat.dto.*;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.SupplierInfoDO;
import cn.iocoder.yudao.module.system.dal.dataobject.biz.WechatMessageDO;
import cn.iocoder.yudao.module.system.dal.dataobject.social.SocialClientDO;
import cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO;
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
import cn.iocoder.yudao.module.system.dal.mysql.biz.WechatMessageMapper;
import cn.iocoder.yudao.module.system.dal.mysql.social.SocialClientMapper;
import cn.iocoder.yudao.module.system.dal.mysql.tenant.TenantMapper;
import cn.iocoder.yudao.module.system.dal.mysql.user.AdminUserMapper;
import cn.iocoder.yudao.module.system.enums.social.SocialTypeEnum;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.util.http.HttpClientResult;
import cn.iocoder.yudao.module.system.util.http.HttpClientUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
import static cn.iocoder.yudao.module.system.dal.dataobject.tenant.TenantDO.PACKAGE_ID_SYSTEM;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.system.enums.oauth2.EnterpriseWeChatConstants.*;

/**
 * @Author: jinyd
 * @CreateTime: 2024/11/27  8:53
 * @Description:
 * @Version: 4.0
 */

@Service
@Slf4j
public class CorpWechatApiImpl implements CorpWechatApi{

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private SocialClientMapper socialClientMapper;

    @Resource
    private WechatMessageMapper wechatMessageMapper;

    @Resource
    private AdminUserMapper userMapper;

    @Resource
    private TenantMapper tenantMapper;

    @Override
    @TenantIgnore
    public String getAccessToken(Long tenantId) {
        if (null == tenantId) {
            TenantDO tenantDO = tenantMapper.selectOne(new LambdaQueryWrapperX<TenantDO>().eq(TenantDO::getPackageId, PACKAGE_ID_SYSTEM));
            tenantId = tenantDO.getId();
        }
        SocialClientDO client = socialClientMapper.selectOne(SocialTypeEnum.WECHAT_ENTERPRISE.getType(), UserTypeEnum.ADMIN.getValue(), tenantId);
        Assert.notNull(client, String.format("第三方(%d) 不存在", SocialTypeEnum.WECHAT_ENTERPRISE.getType()));
        String accessToken = "";
        String rKey = String.format(redisKey, client.getClientId(), client.getAgentId());
        log.info("getAccessToken key:" + rKey);
        Object reddisValue = stringRedisTemplate.opsForValue().get(rKey);
        if (reddisValue != null){
            accessToken = reddisValue.toString();
            log.info("getAccessToken accessToken:" + accessToken);
        }
        if (StringUtil.isEmptyORNull(accessToken)) {
            String url = CORP_WECHAT_URL + GET_TOKEN_URL;
            Map<String, String> map = new HashMap<>();
            map.put("corpid", client.getClientId());
            map.put("corpsecret", client.getClientSecret());
            try{
                HttpClientResult httpClientResult = HttpClientUtil.doGet(url, map, null);
                String content = httpClientResult.getContent();
                log.info("getAccessToken content:" + content);
                CorpWeChatAccessToken corpWeChatAccessToken = JSON.parseObject(content, CorpWeChatAccessToken.class);
                if (corpWeChatAccessToken.getErrcode() == 0) {
                    log.info("getAccessToken corpWeChatAccessToken.getAccessToken():" + corpWeChatAccessToken.getAccessToken());
                    stringRedisTemplate.opsForValue().set(rKey, corpWeChatAccessToken.getAccessToken(), corpWeChatAccessToken.getExpiresIn() - TimeUtil.ONE_MINUTE, TimeUnit.SECONDS);
                    accessToken = corpWeChatAccessToken.getAccessToken();
                }
            } catch (Exception e) {
                log.error("获取企微AccessToken发生错误: ", e);
            }

        }
        return accessToken;
    }

    @Override
    @TenantIgnore
    public CorpUserResponse getUserByUserTicket(String userTicket) {
        CorpUserResponse corpUserResponse = null;
        String accessToken = getAccessToken(null);
        String url = String.format(GET_USER_BY_CODE_URL, accessToken);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("user_ticket", userTicket);
        String msg = jsonObject.toJSONString();
        log.info("请求参数: {}", msg);
        try{
            HttpClientResult httpClientResult = HttpClientUtil.doPostJson(url, msg, null);
            String content = httpClientResult.getContent();
            log.info("请求返回数据: {}", content);
            corpUserResponse = JSON.parseObject(content, CorpUserResponse.class);
            log.info("corpUserResponse :" + corpUserResponse);
            if (corpUserResponse.getErrcode() == 0) {
                log.info("获取访问用户敏感信息成功,手机号为:{} ", corpUserResponse.getMobile());
            } else {
                String errorMsg = "获取访问用户敏感信息失败，错误码为: " + corpUserResponse.getErrcode() + ",错误描述为：" + corpUserResponse.getErrmsg();
                log.error(errorMsg);
                throw exception(CORP_GET_USER_BY_CODE_ERROR, errorMsg);
            }
        } catch (Exception e) {
            log.error("获取访问用户敏感信息发生错误: ", e);
            throw exception(CORP_GET_USER_BY_CODE_ERROR);
        }
        return corpUserResponse;
    }

    @Override
    @TenantIgnore
    public CorpUserResponse getUserByCode(String code) {
        CorpUserResponse corpUserResponse = new CorpUserResponse();
        Map<String, String> map = new HashMap<>();
        String accessToken = getAccessToken(null);
        String url = String.format(GET_USER_INFO_URL, accessToken);
        map.put("code", code);
        String result = "";
        try{
            HttpClientResult httpClientResult = HttpClientUtil.doGet(url, map, null);
            String content = httpClientResult.getContent();
            result = content;
            log.info("请求返回数据: {}", content);
            corpUserResponse = JSON.parseObject(content, CorpUserResponse.class);
            if (corpUserResponse.getErrcode() == 0 && StringUtil.isNotEmptyORNull(corpUserResponse.getUserid())) {
                log.info("获取访问用户身份成功，企微id为: {}", corpUserResponse.getUserid());
            } else {
                String errorMsg = "获取访问用户身份失败，错误码为: " + corpUserResponse.getErrcode() + ",错误描述为：" + corpUserResponse.getErrmsg();
                log.error(errorMsg);
                throw exception(CORP_GET_USER_INFO_ERROR, errorMsg);
            }
        } catch (Exception e) {
            log.info("请求返回数据: {}", result);
            log.error("获取访问用户身份发生错误: ", e);
            throw exception(CORP_GET_USER_INFO_ERROR);
        }
        return corpUserResponse;
    }

    @Override
    @Async
    public String sendMessage(CorpSendMessage req) {
        WechatMessageSaveReq wechatMessageSaveReq = req.getWechatMessageSaveReq();
        CorpWechatTextcard textCard = req.getTextcard();
        String msgRes = StringUtil.EMPY_STRING;
        SocialClientDO client = socialClientMapper.selectOne(SocialTypeEnum.WECHAT_ENTERPRISE.getType(),
                UserTypeEnum.ADMIN.getValue(), wechatMessageSaveReq.getStartTenantId());
        Assert.notNull(client, String.format("第三方(%d) 不存在", SocialTypeEnum.WECHAT_ENTERPRISE.getType()));
        String accessToken = getAccessToken(wechatMessageSaveReq.getStartTenantId());
        log.info("请求参数: {}", req);
        if (StringUtil.isNotEmpty(accessToken)) {
            String url = String.format(SEND_MESSAGE_URL, accessToken);
            try {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("touser", req.getTouser());
                jsonObject.put("toparty", StringUtil.EMPY_STRING);
                jsonObject.put("totag",  StringUtil.EMPY_STRING);
                jsonObject.put("msgtype", "textcard");
                jsonObject.put("enable_duplicate_check", 0);
                jsonObject.put("agentid", Integer.valueOf(client.getAgentId()));

                Map<String, Object> textcard = new HashMap<>();
                textcard.put("title", textCard.getTitle());
                textcard.put("description", textCard.getDescription());
                String ssourl = SSO_URL;
                if(StringUtil.isNotEmptyORNull(textCard.getUrl())){
                    ssourl = ssourl + "?todoUrl=" +textCard.getUrl();
                }
                log.info("ssourl:" + ssourl);
                textcard.put("url", ssourl);
                textcard.put("btntxt", textCard.getBtntxt());

                jsonObject.put("textcard", textcard);
                jsonObject.put("result", StringUtil.EMPY_STRING);
                String msg = jsonObject.toJSONString();
                log.info("msg:" + msg);
                HttpClientResult httpClientResult = HttpClientUtil.doPostJson(url, msg, null);
                msgRes = httpClientResult.getContent();
                log.info("result:" + msgRes);
                if (0 == httpClientResult.getCode()) {
                    wechatMessageSaveReq.setState(1);
                    wechatMessageSaveReq.setParams(msg);
                    wechatMessageSaveReq.setCorpMsgId(JSONObject.parseObject(msgRes).getString("msgid"));
                    WechatMessageDO wechatMessageDO = BeanUtils.toBean(wechatMessageSaveReq, WechatMessageDO.class);
                    wechatMessageMapper.insert(wechatMessageDO);
                } else {
                    wechatMessageSaveReq.setState(2);
                    wechatMessageSaveReq.setParams(msg);
                    wechatMessageSaveReq.setCorpMsgId(JSONObject.parseObject(msgRes).getString("msgid"));
                    WechatMessageDO wechatMessageDO = BeanUtils.toBean(wechatMessageSaveReq, WechatMessageDO.class);
                    wechatMessageMapper.insert(wechatMessageDO);
                    log.error("发送应用消息错误，错误编码为：{}，错误描述为：{}", httpClientResult.getCode(), "");
                }
            } catch (Exception e) {
                log.error("发送应用消息错误:", e);
                wechatMessageSaveReq.setState(2);
                WechatMessageDO wechatMessageDO = BeanUtils.toBean(wechatMessageSaveReq, WechatMessageDO.class);
                wechatMessageMapper.insert(wechatMessageDO);
            }
        }
        return msgRes;
    }

    @Override
    public String getUserIdByMobile(String mobile) {
        String accessToken = getAccessToken(null);
        String url = String.format(GET_USER_BY_MOBILE_URL, accessToken);
        Map<String, String> map = new HashMap<>();
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("mobile", mobile);
        String msg = jsonObject.toJSONString();
        log.info("请求参数: {}", msg);
        HttpClientResult httpClientResult = HttpClientUtil.doPostJson(url, msg, null);
        String content = httpClientResult.getContent();
        log.info("请求返回数据: {}", content);
        CorpUserResponse corpUserResponse = JSON.parseObject(content, CorpUserResponse.class);
        log.info("corpUserResponse :" + corpUserResponse);
        if (corpUserResponse.getErrcode() == 0) {
            log.info("根据手机号{}获取用户id成功，userid为{} :" , mobile, corpUserResponse.getUserid());
            return corpUserResponse.getUserid().toString();
        } else {
            String errorMsg = "根据手机号获取访问用户身份失败，错误码为: " + corpUserResponse.getErrcode() + ",错误描述为：" + corpUserResponse.getErrmsg();
            log.error(errorMsg);
            throw exception(CORP_GET_USER_INFO_BY_MOBILE_ERROR, errorMsg);
        }
    }

    @Override
    public Integer syncCorpUserId() {
        // 企微若出错的次数超出企业规模人数的20%，会导致1天不可调用。获取10条测试
        List<AdminUserDO> list = userMapper.selectList(new LambdaQueryWrapperX<AdminUserDO>().eq(AdminUserDO::getStatus, 0)
                .eq(AdminUserDO::getWorkStatus, 0)
                .isNull(AdminUserDO::getCorpUserId)
                .eq(AdminUserDO::getDeleted, false).orderByDesc(AdminUserDO::getUsername).last("LIMIT 10"));
        list.forEach(user -> {
            String mobile = user.getMobile();
            String corpUserId = getUserIdByMobile(mobile);
            if (StringUtil.isNotEmptyORNull(corpUserId)) {
                AdminUserDO updateUser = new AdminUserDO();
                updateUser.setId(user.getId());
                updateUser.setCorpUserId(corpUserId);
                userMapper.updateById(updateUser);
            }
        });
        return list.size();
    }

}
