package cn.iocoder.yudao.module.system.service.dingding;

import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.tenant.core.util.TenantUtils;
import cn.iocoder.yudao.module.employee.api.EmployeeApi;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeCreateReqDTO;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeQueryReqDTO;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeRespDTO;
import cn.iocoder.yudao.module.employee.api.dto.EmployeeSaveReqDTO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantDingSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantJoinReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.tenant.vo.tenant.TenantSimpleRespVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.tenant.UserTenantRelateSaveReqVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSaveReqVO;
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.enums.dingding.Constants;
import cn.iocoder.yudao.module.system.enums.dingding.SyncHttpConstant;
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.user.UserTenantRelateService;
import cn.iocoder.yudao.module.system.util.dingding.RedisCache;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.dingtalk.api.response.OapiV2UserGetResponse;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptException;
import com.dingtalk.oapi.lib.aes.DingTalkEncryptor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
import static cn.iocoder.yudao.module.system.enums.dingding.CallbackConstant.*;
import static java.util.Collections.singleton;


/**
 * <p>DingAuthServiceImpl 此类用于：</p>
 * <p>@author：hujm</p>
 * <p>@date：2021年05月20日 15:56</p>
 * <p>@remark：</p>
 */
@Slf4j
@Service
public class DingAuthServiceInfoImpl implements DingAuthServiceInfo {

    @Resource
    private RedisCache redisCache;
    @Resource
    private TenantService tenantService;

    @Resource
    private AdminUserService userService;


    @Override
    public String decryptText(String signature, String timestamp, String nonce, String encryptMsg) {
        String plainText = "";
        try {
            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(SyncHttpConstant.TOKEN, SyncHttpConstant.ENCODING_AES_KEY, SyncHttpConstant.SUITE_KEY);
            plainText = dingTalkEncryptor.getDecryptMsg(signature, timestamp, nonce, encryptMsg);
        } catch (DingTalkEncryptException e) {
            log.error("钉钉消息体解密错误, signature: {}, timestamp: {}, nonce: {}, encryptMsg: {}, e: {}", signature, timestamp, nonce, encryptMsg, e);
        }
        log.debug("钉钉消息体解密, signature: {}, timestamp: {}, nonce: {}, encryptMsg: {}, 解密结果: {}", signature, timestamp, nonce, encryptMsg, plainText);
        return plainText;
    }
    @Override
    public Map<String, String> caseProcess(JSONObject plainNode) {
        Map<String, String> resultMap = new LinkedHashMap<>();
        String eventType = plainNode.getString("EventType");
        JSONArray bizData = plainNode.getJSONArray("bizData");
        String corp_id = null;
        JSONObject biz_data = null;
        Integer bizType = 0;
        if(bizData != null){
            for (Object bizDatum : bizData) {
                JSONObject bizDataJson = (JSONObject) bizDatum;
                log.info("[callback] 获取到授权bizDataJson:" + bizDataJson);
                String biz_data_str = bizDataJson.getString("biz_data");
                biz_data = JSONObject.parseObject(biz_data_str);
                bizType = bizDataJson.getInteger("biz_type");
                log.info("[callback] 获取到bizType:" + bizType);
                corp_id = bizDataJson.getString("corp_id");
            }
        }

        switch (eventType) {
            case SUITE_TICKET_CALLBACK_URL_VALIDATE:
                log.info("[callback] 验证回调地址有效性质:{}", plainNode);
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            case TEMP_AUTH_CODE_ACTIVE:
                log.info("[callback] 企业开通授权:{}", plainNode);
                log.info("[callback] 企业开通授权信息：", bizData);
                if (bizType == 4 && biz_data != null && corp_id != null) {
//                    updateCompanyInfo(biz_data,corp_id);
                }
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            case SUITE_RELIEVE:
                log.info("[callback] 企业解除授权:{}", plainNode);
                if (bizType == 4 && biz_data != null && corp_id != null) {
//                    tEnterpriseCompanyService.updateTCompanyUserClose(corp_id);
                }
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            case CHECK_UPDATE_SUITE_URL:
                log.info("[callback] 在开发者后台修改回调地址:" + plainNode);
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            case CHECK_CREATE_SUITE_URL:
                log.info("[callback] 检查钉钉向回调URL POST数据解密后是否成功:" + plainNode);
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            case CONTACT_CHANGE_AUTH:
                log.info("[callback] 通讯录授权范围变更事件:" + plainNode);
                break;
            case ORG_MICRO_APP_STOP:
                log.info("[callback] 停用应用:" + plainNode);
                break;
            case ORG_MICRO_APP_RESTORE:
                log.info("[callback] 启用应用:" + plainNode);
                break;
            case MARKET_BUY:
                log.info("[callback] 用户下单购买事件:" + plainNode);
                break;
            case SYNC_HTTP_PUSH_HIGH:
                log.info("[callback] 套件票据事件:" + plainNode);
                for (Object bizDatum : bizData) {
                    JSONObject bizDataJson = (JSONObject) bizDatum;
                    log.info("[callback] 获取到授权bizDataJson:" + bizDataJson);
                    String biz_data_str = bizDataJson.getString("biz_data");
                    biz_data = JSONObject.parseObject(biz_data_str);
                    bizType = bizDataJson.getInteger("biz_type");
                    log.info("[callback] 获取到bizType:" + bizType);
                    corp_id = bizDataJson.getString("corp_id");
                }
                log.info("[callback] 获取到套件票据corp_id:{}", corp_id);
                //套件票据
                if (bizType == 2 && biz_data != null && corp_id != null) {
                    String suiteTicket = biz_data.getString("suiteTicket");
//                    String redisKeyPrefix = Constants.DAILY_DING_AUTH + corp_id + ":";
//                    redisCache.setCacheObject(redisKeyPrefix + Constants.DAILY_DING_SUITE_TICKET, suiteTicket, 5, TimeUnit.HOURS);
                    //todo 更新票据-数据为第三方企业应用票据最新suiteTicket，定时每5个小时推送一次。
                    tenantService.updateTenantSuiteTicket(corp_id,suiteTicket);
                }
                //套件票据授权
                if (bizType == 4 && biz_data != null && corp_id != null) {
                    //syncAction字段的取值如下
                    //org_suite_auth：表示企业授权第三方企业应用
                    //org_suite_change：表示企业变更授权范围
                    //org_suite_relieve：表示企业解除授权
                    //企业授权信息
                    String syncAction = biz_data.getString("syncAction");
                    if ("org_suite_auth".equals(syncAction)) {
                        log.info("[callback] 企业授权第三方企业应用:{}",corp_id);
                        TenantDO tenant = tenantService.getTenantByCorpId(corp_id);
                        if (tenant == null || tenant.getId() == null) {
                            this.createTenant(biz_data, corp_id);
                        }else{
                            this.updateTenant(biz_data, corp_id);
                        }
                    } else if ("org_suite_change".equals(syncAction)) {
                        log.info("[callback] 表示企业变更授权范围:{}",corp_id);
                        this.updateTenant(biz_data, corp_id);
                    } else if ("org_suite_relieve".equals(syncAction)) {
                        log.info("[callback] 表示企业解除授权:{}",corp_id);
                        //todo 暂时不处理
//                        this.deleteTenant(biz_data, corp_id);
                    }
                }
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            case CHECK_URL:
                log.info("[callback] 检查钉钉向回调URL POST数据解密后是否成功:" + plainNode);
                resultMap = encryptText(CALLBACK_RETURN_SUCCESS);
                break;
            default:
                log.info("[callback] 未知事件: {} , 内容: {}", eventType, plainNode);
                resultMap = encryptText("事件类型未定义, 请联系应用提供方!" + eventType);
                break;
        }
        return resultMap;
    }
    @Override
    public Map<String, String> encryptText(String text) {
        Map<String, String> resultMap = new LinkedHashMap<>();
        try {
            DingTalkEncryptor dingTalkEncryptor = new DingTalkEncryptor(SyncHttpConstant.TOKEN, SyncHttpConstant.ENCODING_AES_KEY, SyncHttpConstant.SUITE_KEY);
            resultMap = dingTalkEncryptor.getEncryptedMap(text, System.currentTimeMillis(), getRandomStr(8));
        } catch (DingTalkEncryptException e) {
            log.error("钉钉消息体加密,text: {}, e: {}", text, e);
        }
        log.debug("钉钉消息体加密,text: {}, resultMap: {}", text, resultMap);
        return resultMap;
    }

    /**
     * 钉钉初始化用户单点登录接口
     * @param userSaveReqVO
     * @param corpId
     * @param adminType 是否是管理员
     */
    @Override
    public AdminUserDO initUser(UserSaveReqVO userSaveReqVO, String corpId, Boolean adminType) {

        //查询租户
        TenantDO tenant = tenantService.getTenantByCorpId(corpId);
        if (tenant == null || tenant.getId() == null) {
            throw exception(TENANT_NOT_EXISTS);
        }
        // 校验账号是否存在
        AdminUserDO user = userService.getUserByUsername(userSaveReqVO.getMobile());
        if (user != null) {
            throw exception(USER_USERNAME_EXISTS);
        }

        Long id = userService.createDDingUser(userSaveReqVO,tenant.getId());
        // 获得新用户信息
        user = userService.getUserByMobile(userSaveReqVO.getMobile());
        if (user == null) {
            throw exception(USER_NOT_EXISTS);
        }

        if(adminType){
            //执行租户信息初始化操作
            tenantService.createDDingInit(user,tenant);
        }else{
            //todo 更新用户租户操作
            TenantJoinReqVO createReqVO = new TenantJoinReqVO();
            createReqVO.setTenantId(tenant.getId());
            createReqVO.setCorpId(corpId);
            createReqVO.setUserId(user.getId());
            tenantService.joinDDingTenant(createReqVO);
        }
        return user;
    }


    public static String getRandomStr(int count) {
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();

        for (int i = 0; i < count; ++i) {
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        }

        return sb.toString();
    }

    /**
     * 创建租户
     *
     * @param biz_data
     * @param corp_id
     */
    private void createTenant(JSONObject biz_data, String corp_id) {
        String authCorpInfo_str = biz_data.getString("auth_corp_info");//授权方企业信息。
        String auth_info_str = biz_data.getString("auth_info");//授权信息。
        String auth_user_info_str = biz_data.getString("auth_user_info");//授权方管理员信息
        JSONObject authCorpInfo = JSONObject.parseObject(authCorpInfo_str);
        JSONObject authInfo = JSONObject.parseObject(auth_info_str);
        JSONArray agentArray = authInfo.getJSONArray("agent");
        JSONObject agent = JSONObject.parseObject(agentArray.get(0).toString());
        JSONObject authUserInfo = JSONObject.parseObject(auth_user_info_str);
//        String accessToken = dingThirdTokenService.getCorpAccessToken(corp_id);
        String manager_userid = authUserInfo.getString("userId");
        TenantDingSaveReqVO respVO = new TenantDingSaveReqVO();
        respVO.setCorpId(authCorpInfo.getString("corpid"));
        respVO.setName(authCorpInfo.getString("full_corp_name"));
        respVO.setManageUserId(manager_userid);
        respVO.setExpireTime(LocalDateTime.now().plusYears(10));// 默认设置10年有效期
        tenantService.createDingTenant(respVO);
    }

    /**
     * 更新租户
     *
     * @param biz_data
     * @param corp_id
     */
    private void updateTenant(JSONObject biz_data, String corp_id) {
        String authCorpInfo_str = biz_data.getString("auth_corp_info");//授权方企业信息。
        String auth_info_str = biz_data.getString("auth_info");//授权信息。
        String auth_user_info_str = biz_data.getString("auth_user_info");//授权方管理员信息
        JSONObject authCorpInfo = JSONObject.parseObject(authCorpInfo_str);
        JSONObject authInfo = JSONObject.parseObject(auth_info_str);
        JSONArray agentArray = authInfo.getJSONArray("agent");
        JSONObject agent = JSONObject.parseObject(agentArray.get(0).toString());
        JSONObject authUserInfo = JSONObject.parseObject(auth_user_info_str);
//        String accessToken = dingThirdTokenService.getCorpAccessToken(corp_id);
        String manager_userid = authUserInfo.getString("userId");
        TenantDingSaveReqVO respVO = new TenantDingSaveReqVO();
        respVO.setCorpId(authCorpInfo.getString("corpid"));
        respVO.setName(authCorpInfo.getString("full_corp_name"));
        respVO.setManageUserId(manager_userid);
        tenantService.updateDingTenant(respVO);
    }

    /**
     * 删除租户
     *
     * @param biz_data
     * @param corp_id
     */
    private void deleteTenant(JSONObject biz_data, String corp_id) {
        String authCorpInfo_str = biz_data.getString("auth_corp_info");//授权方企业信息。
        String auth_info_str = biz_data.getString("auth_info");//授权信息。
        String auth_user_info_str = biz_data.getString("auth_user_info");//授权方管理员信息
        JSONObject authCorpInfo = JSONObject.parseObject(authCorpInfo_str);
        JSONObject authInfo = JSONObject.parseObject(auth_info_str);
        JSONArray agentArray = authInfo.getJSONArray("agent");
        JSONObject agent = JSONObject.parseObject(agentArray.get(0).toString());
        JSONObject authUserInfo = JSONObject.parseObject(auth_user_info_str);
//        String accessToken = dingThirdTokenService.getCorpAccessToken(corp_id);
        String manager_userid = authUserInfo.getString("userId");
        TenantDingSaveReqVO respVO = new TenantDingSaveReqVO();
        respVO.setCorpId(authCorpInfo.getString("corpid"));
        tenantService.deleteDingTenant(respVO);
    }

}