package com.yida.data.system.service.impl;

import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yida.data.common.core.entity.constant.AppConstant;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.constant.QywxConstant;
import com.yida.data.common.core.entity.constant.QywxServiceProviderConstant;
import com.yida.data.common.core.entity.notice.qywx.BaseStaffNotice;
import com.yida.data.common.core.entity.notice.qywx.inside.Text;
import com.yida.data.common.core.entity.system.*;
import com.yida.data.common.core.entity.user.EduStaff;
import com.yida.data.common.core.enums.QywxAppTypeEnum;
import com.yida.data.common.core.utils.WxServiceProviderUtil;
import com.yida.data.common.core.utils.WxUtil;
import com.yida.data.common.service.CommonService;
import com.yida.data.system.service.*;
import com.yida.data.user.feign.RemoteStaffService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import java.util.Objects;

/**
 * 企业微信服务商处理方法service
 *
 * @author ZYJ
 * @date 2022/11/3
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(rollbackFor = Exception.class)
public class QywxServiceProviderServiceImpl implements QywxServiceProviderService {

    private final QywxService qywxService;
    private final IDeptService deptService;
    private final EduAppService eduAppService;
    private final EduQywxServiceProviderService eduQywxServiceProviderService;
    private final EduQywxServiceProviderSchoolService eduQywxServiceProviderSchoolService;

    private final WxUtil wxUtil;
    private final RedisService redisService;
    private final CommonService commonService;
    private final WxServiceProviderUtil wxServiceProviderUtil;

    private final RemoteStaffService remoteStaffService;

    @Value("${febs.authRedirectUrl}")
    private String authRedirectUrl;

    @Override
    public void installTemplateApp(String suiteId, String authCode, EduQywxServiceProvider serviceProvider) {
        Object oSuiteTicket = redisService.get(QywxServiceProviderConstant.SERVICE_SUITE_TICKET + suiteId);
        if (Objects.isNull(oSuiteTicket)) {
            log.error("安装授权应用失败, suiteId: {}, authCode: {}", suiteId, authCode);
            return;
        }
        // 处理授权的企业微信信息
        handleAuthQywx(suiteId, authCode, String.valueOf(oSuiteTicket), serviceProvider);
    }

    /**
     * 处理授权的企业微信信息
     */
    @Async
    public void handleAuthQywx(String suiteId, String authCode, String suiteTicket, EduQywxServiceProvider serviceProvider) {
        // 获取应用模板信息
        EduAppTemplate appTemplate = commonService.getAppTemplateByTemplateId(suiteId, serviceProvider.getCorpId());
        // 获取第三方应用凭证
        String suiteAccessToken = wxServiceProviderUtil.getSuiteAccessToken(appTemplate.getTemplateId(),
                appTemplate.getTemplateSecret(), suiteTicket);
        // 获取企业永久授权码
        JSONObject jsonObject = wxServiceProviderUtil.getPermanentCode(suiteAccessToken, authCode);
        // 获取授权企业微信id密文
        String authCorpId = jsonObject.getJSONObject("auth_corp_info").getStr("corpid");
        // 获取企业微信信息
//        EduQywxServiceProviderSchool providerSchool = (EduQywxServiceProviderSchool) redisService.hget(QywxServiceProviderConstant.PROVIDER_SCHOOL_CORP_DATA, serviceProvider.getCorpId() + "." + authCorpId);
        EduQywxServiceProviderSchool providerSchool = commonService.getCorpByEncryptionData(serviceProvider.getCorpId(), authCorpId);
        log.info("匹配的corpId信息: {}", providerSchool);
        // 查询对应的部门信息
        Dept dept = deptService.getOne(Wrappers.lambdaQuery(new Dept()).eq(Dept::getCorpId, providerSchool.getDeptOriginalCorpId()));
        // 生成企业应用信息
        EduApp eduApp = new EduApp();
        eduApp.setDeptId(dept.getDeptId());
        eduApp.setName(appTemplate.getTemplateName());
        eduApp.setCode(appTemplate.getTemplateCode());
        eduApp.setWxCorpId(dept.getCorpId());
        eduApp.setAppType(QywxAppTypeEnum.SERVICE_PROVIDER_APPLICATION_TYPE.getType());
        eduApp.setTemplateId(appTemplate.getTemplateId());
        eduApp.setWxSecret(jsonObject.getStr("permanent_code"));
        eduApp.setServiceCorpId(serviceProvider.getCorpId());
        // 应用信息
        JSONArray agentArray = jsonObject.getJSONObject("auth_info").getJSONArray("agent");
        if (CollUtil.isNotEmpty(agentArray)) {
            Object object = agentArray.get(0);
            JSONObject obj = JSONUtil.parseObj(object);
            eduApp.setWxAgentId(obj.getStr("agentid"));
        }
        eduAppService.save(eduApp);
    }

    @Override
    public void handleTemplateSuiteTicket(String suiteId, String suiteTicket) {
        redisService.set(QywxServiceProviderConstant.SERVICE_SUITE_TICKET + suiteId, suiteTicket);
    }

    @Override
    public void deleteTemplateApp(String suiteId, String authCorpId, EduQywxServiceProvider serviceProvider) {
        // 获取企业微信信息
//        EduQywxServiceProviderSchool providerSchool = (EduQywxServiceProviderSchool) redisService.hget(QywxServiceProviderConstant.PROVIDER_SCHOOL_CORP_DATA, serviceProvider.getCorpId() + "." + authCorpId);
        EduQywxServiceProviderSchool providerSchool = commonService.getCorpByEncryptionData(serviceProvider.getCorpId(), authCorpId);
        // 查询对应的部门信息
        Dept dept = deptService.getOne(Wrappers.lambdaQuery(new Dept()).eq(Dept::getCorpId, providerSchool.getDeptOriginalCorpId()));
        // 查询对应应用信息
        EduApp eduApp = eduAppService.getOne(Wrappers.lambdaQuery(new EduApp())
                .eq(EduApp::getDeptId, dept.getDeptId())
                .eq(EduApp::getWxCorpId, dept.getCorpId())
                .eq(EduApp::getTemplateId, suiteId));
        // 删除对应应用
        eduAppService.removeById(eduApp.getId());
        redisService.hdel(CachePrefixConstant.SYS_APP + dept.getDeptId(), eduApp.getCode());
    }

    @Override
    public void resetTemplateAppAuthCode(String suiteId, String resetAuthCode, EduQywxServiceProvider serviceProvider) {
        Object oSuiteTicket = redisService.get(QywxServiceProviderConstant.SERVICE_SUITE_TICKET + suiteId);
        // 获取应用模板信息
        EduAppTemplate appTemplate = commonService.getAppTemplateByTemplateId(suiteId, serviceProvider.getCorpId());
        // 获取第三方应用凭证
        String suiteAccessToken = wxServiceProviderUtil.getSuiteAccessToken(appTemplate.getTemplateId(),
                appTemplate.getTemplateSecret(), String.valueOf(oSuiteTicket));
        // 获取企业永久授权码
        JSONObject jsonObject = wxServiceProviderUtil.getPermanentCode(suiteAccessToken, resetAuthCode);
        // 获取授权企业微信id密文
        String authCorpId = jsonObject.getJSONObject("auth_corp_info").getStr("corpid");
        // 获取企业微信信息
//        EduQywxServiceProviderSchool providerSchool = (EduQywxServiceProviderSchool) redisService.hget(QywxServiceProviderConstant.PROVIDER_SCHOOL_CORP_DATA, serviceProvider.getCorpId() + "." + authCorpId);
        EduQywxServiceProviderSchool providerSchool = commonService.getCorpByEncryptionData(serviceProvider.getCorpId(), authCorpId);

        eduAppService.update(Wrappers.lambdaUpdate(new EduApp())
                .eq(EduApp::getWxCorpId, providerSchool.getDeptOriginalCorpId())
                .eq(EduApp::getTemplateId, suiteId)
                .set(EduApp::getWxSecret, jsonObject.getStr("permanent_code")));

        // 查询对应的部门信息
        Dept dept = deptService.getOne(Wrappers.lambdaQuery(new Dept()).eq(Dept::getCorpId, providerSchool.getDeptOriginalCorpId()));
        // 处理应用缓存
        EduApp eduApp = eduAppService.getOne(Wrappers.lambdaQuery(new EduApp())
                .eq(EduApp::getDeptId, dept.getDeptId())
                .eq(EduApp::getWxCorpId, dept.getCorpId())
                .eq(EduApp::getTemplateId, suiteId));
        redisService.hset(CachePrefixConstant.SYS_APP + dept.getDeptId(), eduApp.getCode(), eduApp);
    }

    @Override
    public void handleConcat(Element root, EduQywxServiceProvider serviceProvider, String suiteId) {
        // 授权企业加密corpId
        NodeList nodeListAuthCorpId = root.getElementsByTagName("AuthCorpId");
        String authCorpId = nodeListAuthCorpId.item(0).getTextContent();
        // 获取企业微信信息
        EduQywxServiceProviderSchool providerSchool = commonService.getCorpByEncryptionData(serviceProvider.getCorpId(), authCorpId);

        // 查询对应的学校信息
        Dept school = deptService.getOne(Wrappers.lambdaQuery(new Dept())
                .eq(Dept::getCorpId, providerSchool.getDeptOriginalCorpId()));
        // 此处获取对应学校通讯录(人力资源)应用
        EduApp app = commonService.getAppByCodeAndSchool(AppConstant.CONTACT_SELECT, null, school.getDeptId());

        // 操作通讯录人员
        String userId = null;
        if (root.getElementsByTagName("UserID").getLength() != 0) {
            userId = root.getElementsByTagName("UserID").item(0).getTextContent();
        }

        // 操作通讯录部门
        String deptId = null;
        String deptParentId = null;
        if (root.getElementsByTagName("Id").getLength() != 0) {
            deptId = root.getElementsByTagName("Id").item(0).getTextContent();
        }
        if (root.getElementsByTagName("ParentId").getLength() != 0) {
            deptParentId = root.getElementsByTagName("ParentId").item(0).getTextContent();
        }

        // 获取修改类型
        NodeList nodeListChangeType = root.getElementsByTagName("ChangeType");
        String changeType = nodeListChangeType.item(0).getTextContent();
        switch (changeType) {
            case QywxConstant.USER_CREATE_TYPE:
                // 查询用户数据是否已存在
                EduStaff eduStaff = remoteStaffService.getStaffByWxOrMobileOrOpenId(userId, null, null, school.getDeptId()).getData();
                if (Objects.nonNull(eduStaff)) {
                    break;
                }
                String authUrl = commonService.setAuthUrl(school.getCorpId(), authRedirectUrl + "?schoolId=" + school.getDeptId(), app.getWxAgentId());
                // 发送授权消息给创建人员
                BaseStaffNotice textSchoolNotice = new BaseStaffNotice();
                Text text = new Text();
                textSchoolNotice.setText(text);
                textSchoolNotice.setAgentid(app.getWxAgentId());
                text.setContent("<a href='" + authUrl + "'>智慧校园系统信息授权：请点击授权（请务必授权手机号！）</a>");
                textSchoolNotice.setTouser(userId);
                wxUtil.pushStaffNotice(wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret()), textSchoolNotice);
                break;
            case QywxConstant.USER_UPDATE_TYPE:
                // 仅在成员修改部门时回调
//                qywxService.updateUserInfo(userId, school, app);
                break;
            case QywxConstant.USER_DELETE_TYPE:
                qywxService.deleteUser(userId, school.getDeptId());
                break;
            case QywxConstant.DEPT_CREATE_TYPE:
                // 获取单个部门详情
                JSONObject deptInfo = wxUtil.getDeptInfo(wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret()), deptId);
                String deptName = deptInfo.get("name").toString();
                String order = deptInfo.get("order").toString();
                qywxService.createDept(deptId, deptName, deptParentId, order, school);
                break;
            case QywxConstant.DEPT_UPDATE_TYPE:
                // 仅在变更父部门时回调，且部门属性仅回调Id/ParentId两个字段
                // 获取单个部门详情
//                JSONObject updateDeptInfo = wxUtil.getDeptInfo(wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret()), deptId);
//                qywxService.updateDept(deptId, updateDeptInfo.get("name").toString(), deptParentId, school.getDeptId());
                break;
            case QywxConstant.DEPT_DELETE_TYPE:
                qywxService.deleteDept(deptId, school.getDeptId());
                break;
            default:
                break;
        }
    }

    @Override
    public void handleConcat(Element root, EduQywxServiceProviderSchool providerSchool) {
        // 查询对应的学校信息
        Dept school = deptService.getOne(Wrappers.lambdaQuery(new Dept())
                .eq(Dept::getCorpId, providerSchool.getDeptOriginalCorpId()));
        // 此处获取对应学校通讯录(人力资源)应用
        EduApp app = commonService.getAppByCodeAndSchool(AppConstant.CONTACT_SELECT, null, school.getDeptId());

        // 操作通讯录人员
        String userId = null;
        if (root.getElementsByTagName("UserID").getLength() != 0) {
            userId = root.getElementsByTagName("UserID").item(0).getTextContent();
        }

        // 操作通讯录部门
        String deptId = null;
        String deptParentId = null;
        if (root.getElementsByTagName("Id").getLength() != 0) {
            deptId = root.getElementsByTagName("Id").item(0).getTextContent();
        }
        if (root.getElementsByTagName("ParentId").getLength() != 0) {
            deptParentId = root.getElementsByTagName("ParentId").item(0).getTextContent();
        }
        // 获取修改类型
        NodeList nodeListChangeType = root.getElementsByTagName("ChangeType");
        String changeType = nodeListChangeType.item(0).getTextContent();
        switch (changeType) {
            case QywxConstant.USER_UPDATE_TYPE:
                // 仅在成员修改部门时回调
                qywxService.updateUserInfo(userId, school, app);
                break;
            case QywxConstant.DEPT_UPDATE_TYPE:
                // 仅在变更父部门时回调，且部门属性仅回调Id/ParentId两个字段
                // 获取单个部门详情
                JSONObject updateDeptInfo = wxUtil.getDeptInfo(wxUtil.getAccessToken(app.getWxCorpId(), app.getWxSecret()), deptId);
                qywxService.updateDept(deptId, updateDeptInfo.get("name").toString(), deptParentId, school.getDeptId());
                break;
            default:
                break;
        }
    }

    @Override
    public void handleSchoolConcat(Element root, EduQywxServiceProviderSchool providerSchool, Dept school) {
        // 获取修改类型
        NodeList nodeListChangeType = root.getElementsByTagName("ChangeType");
        String changeType = nodeListChangeType.item(0).getTextContent();
        // 此处获取对应学校家校通讯录应用
        EduApp app = commonService.getAppByCodeAndSchool(AppConstant.CONTACT_SCHOOL, null, school.getDeptId());
        String id = root.getElementsByTagName("Id").item(0).getTextContent();
        // 学校对应corpId
        String corpId = providerSchool.getDeptOriginalCorpId();

        switch (changeType) {
            case QywxConstant.SCHOOL_DEPT_CREATE_TYPE:
                qywxService.createSchoolDept(corpId, wxUtil.getAccessToken(app.getWxCorpId(),
                        app.getWxSecret()), Long.valueOf(id), school);
                break;
            case QywxConstant.SCHOOL_DEPT_UPDATE_TYPE:
                qywxService.updateSchoolDept(wxUtil.getAccessToken(app.getWxCorpId(),
                        app.getWxSecret()), Long.valueOf(id), school);
                break;
            case QywxConstant.SCHOOL_DEPT_DELETE_TYPE:
                qywxService.deleteSchoolDept(Long.valueOf(id), school);
                break;
            case QywxConstant.STUDENT_CREATE_TYPE:
            case QywxConstant.STUDENT_UPDATE_TYPE:
                // 判断是否为修改学生唯一键. 修改id会返回NewId字段作为唯一键
                if (root.getElementsByTagName("NewId").getLength() != 0) {
                    id = root.getElementsByTagName("NewId").item(0).getTextContent();
                }
                qywxService.changeStudent(wxUtil.getAccessToken(app.getWxCorpId(),
                        app.getWxSecret()), id, school);
                break;
            case QywxConstant.STUDENT_DELETE_TYPE:
                qywxService.deleteStudent(id, school.getDeptId());
                break;
            case QywxConstant.PARENT_CREATE_TYPE:
                // 新增家长可能是创建学生时创建，等待2s确保学生创建完成
                ThreadUtil.sleep(2000);
            case QywxConstant.PARENT_UPDATE_TYPE:
                qywxService.updateParent(wxUtil.getAccessToken(app.getWxCorpId(),
                        app.getWxSecret()), id, school.getDeptId());
                break;
            case QywxConstant.PARENT_DELETE_TYPE:
                qywxService.deleteParent(id, school.getDeptId());
                break;
            case QywxConstant.PARENT_SUBSCRIBE_TYPE:
                log.info("进入家长关注事件回调");
                // 调用家长关注发送通知方法
                qywxService.sendParentSubscribeMessage(id, school.getDeptId());
                break;
            case QywxConstant.PARENT_UNSUBSCRIBE_TYPE:
                log.info("进入家长取消关注事件回调");
                break;
            default:
                break;
        }
    }

    @Override
    public void changeCorpId(String providerCorpId, String corpId) {
        // 查询是否有关联数据
        EduQywxServiceProviderSchool providerSchool = eduQywxServiceProviderSchoolService.getOne(Wrappers.lambdaQuery(new EduQywxServiceProviderSchool())
                .eq(EduQywxServiceProviderSchool::getProviderCorpId, providerCorpId)
                .eq(EduQywxServiceProviderSchool::getDeptOriginalCorpId, corpId));
        if (Objects.isNull(providerSchool)) {
            providerSchool = new EduQywxServiceProviderSchool();
            providerSchool.setProviderCorpId(providerCorpId);
            providerSchool.setDeptOriginalCorpId(corpId);

            EduQywxServiceProvider provider = eduQywxServiceProviderService.getOne(Wrappers.lambdaQuery(new EduQywxServiceProvider())
                    .eq(EduQywxServiceProvider::getCorpId, providerCorpId));

            // 转换corpId
            String providerToken = wxServiceProviderUtil.getProviderToken(provider.getCorpId(), provider.getServiceProviderSecret());
            String convertCorpId = wxServiceProviderUtil.convertCorpId(providerToken, corpId);
            providerSchool.setDeptEncryptionCorpId(convertCorpId);
            eduQywxServiceProviderSchoolService.save(providerSchool);

            redisService.hset(QywxServiceProviderConstant.PROVIDER_SCHOOL_CORP_DATA,
                    provider.getCorpId() + "." + providerSchool.getDeptEncryptionCorpId(), providerSchool);
            redisService.hset(QywxServiceProviderConstant.PROVIDER_SCHOOL_CORP_ORIGINAL_DATA,
                    provider.getCorpId() + "." + providerSchool.getDeptOriginalCorpId(), providerSchool);
        }
    }
}
