package com.ruoyi.dxp.service.impl;

import java.util.ArrayList;
import java.util.List;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.JSONArray;
import com.alibaba.fastjson2.JSONException;
import com.alibaba.fastjson2.JSONObject;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.RedisUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.dxp.constant.DxpConstants;
import com.ruoyi.dxp.constant.DxpRedisKeyConstants;
import com.ruoyi.dxp.domain.*;
import com.ruoyi.dxp.dto.DataItemResourceDto;
import com.ruoyi.dxp.dto.NoticeSendDto;
import com.ruoyi.dxp.dto.ResourceAuditDto;
import com.ruoyi.dxp.dto.redis.*;
import com.ruoyi.dxp.mapper.*;
import com.ruoyi.dxp.service.INoticeService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ruoyi.dxp.service.IUserResourceService;
import org.springframework.transaction.annotation.Transactional;

/**
 * 用户订阅资源Service业务层处理
 *
 * @author jy
 * @date 2022-12-07
 */
@Service
public class UserResourceServiceImpl implements IUserResourceService {
    @Autowired
    private UserResourceMapper userResourceMapper;

    @Autowired
    private UserResourceItemMapper userResourceItemMapper;

    @Autowired
    private ServiceShareMapper serviceShareMapper;

    @Autowired
    private ServiceIntegrationMapper serviceIntegrationMapper;

    @Autowired
    private DataShareMapper dataShareMapper;

    @Autowired
    private RuleDesensitizationMapper ruleDesensitizationMapper;

    @Autowired
    private ApplicationMapper applicationMapper;

    @Autowired
    private INoticeService noticeService;

    @Autowired
    private RedisCache redisCache;

    /**
     * 查询用户订阅资源
     *
     * @param id 用户订阅资源主键
     * @return 用户订阅资源
     */
    @Override
    public UserResource selectUserResourceById(Long id) {
        return userResourceMapper.selectUserResourceById(id);
    }

    /**
     * 查询用户订阅资源列表
     *
     * @param userResource 用户订阅资源
     * @return 用户订阅资源
     */
    @Override
    public List<UserResource> selectUserResourceList(UserResource userResource) {
        return userResourceMapper.selectUserResourceList(userResource);
    }

    /**
     * 新增用户订阅资源
     *
     * @param userResource 用户订阅资源
     * @return 结果
     */
    @Override
    public int insertUserResource(UserResource userResource) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        userResource.setCreateBy(loginUser.getUsername());
        userResource.setCreateTime(DateUtils.getNowDate());
        return userResourceMapper.insertUserResource(userResource);
    }

    /**
     * 修改用户订阅资源
     *
     * @param userResource 用户订阅资源
     * @return 结果
     */
    @Override
    public int updateUserResource(UserResource userResource) {
        LoginUser loginUser = SecurityUtils.getLoginUser();
        userResource.setUpdateBy(loginUser.getUsername());
        userResource.setUpdateTime(DateUtils.getNowDate());
        return userResourceMapper.updateUserResource(userResource);
    }

    /**
     * 删除用户订阅资源信息
     *
     * @param id 用户订阅资源主键
     * @return 结果
     */
    @Override
    public int deleteUserResourceById(Long id) {
        return userResourceMapper.deleteUserResourceById(id);
    }

    /**
     * 审核
     *
     * @param auditDto
     * @return
     */
    @Override
    @Transactional
    public AjaxResult audit(ResourceAuditDto auditDto) {
        if (StringUtils.isEmpty(auditDto.getStatus())) {
            return AjaxResult.error("审核状态不能为空");
        }

        if (!auditDto.getStatus().equals(DxpConstants.STATUS_VALID) && !auditDto.getStatus().equals(DxpConstants.STATUS_REJECT)) {
            return AjaxResult.error("审核状态错误");
        }

        if (StringUtils.isEmpty(auditDto.getAuditRemark())) {
            return AjaxResult.error("审核原因不能为空");
        }
        UserResource userResource = userResourceMapper.selectUserResourceById(auditDto.getId());
        if (userResource == null) {
            return AjaxResult.error("待审核资源不存在或非待审核状态");
        }

        LoginUser loginUser = SecurityUtils.getLoginUser();
        userResource.setStatus(auditDto.getStatus());
        userResource.setAuditRemark(auditDto.getAuditRemark());
        userResource.setAuditBy(loginUser.getUsername());
        userResource.setAuditTime(DateUtils.getNowDate());
        userResource.setStatusRemark(auditDto.getStatusRemark());
        userResourceMapper.updateUserResource(userResource);

        if (!userResource.getResourceType().equals(DxpConstants.RESOURCE_TYPE_LITERATURE)) {
            UserResourceItem req = new UserResourceItem();
            req.setUserResourceId(auditDto.getId());
            List<UserResourceItem> userResourceItems = userResourceItemMapper.selectUserResourceItemList(req);
            UserResourceItem userResourceItem = new UserResourceItem();
            if (userResourceItems != null && userResourceItems.size() > 0) {
                userResourceItem = userResourceItems.get(0);
                userResourceItem.setResourceItem(auditDto.getResourceItem());
                userResourceItem.setUpdateBy(loginUser.getUsername());
                userResourceItem.setUpdateTime(DateUtils.getNowDate());
                userResourceItemMapper.updateUserResourceItem(userResourceItem);
            } else {
                userResourceItem.setUserResourceId(auditDto.getId());
                userResourceItem.setResourceItem(auditDto.getResourceItem());
                userResourceItem.setCreateBy(loginUser.getUsername());
                userResourceItem.setCreateTime(DateUtils.getNowDate());
                userResourceItemMapper.insertUserResourceItem(userResourceItem);
            }

            // 写入redis
            if (auditDto.getStatus().equals(DxpConstants.STATUS_VALID)) {
                this.writeToRedis(userResource, userResourceItem);
            } else {
                this.delToRedis(userResource.getCallId());
            }
        }

        this.writeToSubscribeRedis();

        // 发送通知
        NoticeSendDto sendDto = new NoticeSendDto();
        sendDto.setNoticeType("auditResult");
        sendDto.setUserId(userResource.getUserId());
        sendDto.setContennt("您申请的资源【" + userResource.getResourceName() + "】，已审核" + (auditDto.getStatus().equals(DxpConstants.STATUS_VALID) ? "通过" : ("拒绝，拒绝原因：" + auditDto.getAuditRemark())));
        noticeService.sendNotice(sendDto);

        return AjaxResult.success();
    }

    /**
     * 写入redis
     *
     * @param userResource     用户订购资源
     * @param userResourceItem 用户订购资源项
     */
    @Override
    public void writeToRedis(UserResource userResource, UserResourceItem userResourceItem) {
        // 订阅应用
        Application application = applicationMapper.selectApplicationById(userResource.getApplicationId());
        RedisApplicationDto applicationDto = new RedisApplicationDto();
        applicationDto.setAppId(application.getAppId());
        applicationDto.setAppSecret(application.getAppSecret());

        if (userResource.getResourceType().equals(DxpConstants.RESOURCE_TYPE_SERVICE)) {
            // 查询服务
            ServiceShare serviceShare = serviceShareMapper.selectServiceShareById(userResource.getResourceId());
            if (serviceShare == null) {
                return;
            }

            // 组装参数
            List<RedisServiceParamDto> serviceParamDtos = new ArrayList<>();
            List<ServiceItem> serviceItems = JSON.parseArray(userResourceItem.getResourceItem(), ServiceItem.class);
            if (serviceItems != null && serviceItems.size() > 0) {
                for (ServiceItem serviceItem : serviceItems) {
                    RedisServiceParamDto redisServiceParamDto = new RedisServiceParamDto();
                    redisServiceParamDto.setParamName(serviceItem.getParamName());
                    redisServiceParamDto.setParamComment(serviceItem.getParamComment());
                    redisServiceParamDto.setParamType(serviceItem.getParamType());
                    redisServiceParamDto.setDataType(serviceItem.getDataType());
                    if (serviceItem.getIsRequired() != null && serviceItem.getIsRequired().equals(DxpConstants.YES)) {
                        redisServiceParamDto.setIsRequired(1);
                    }
                    redisServiceParamDto.setRemark(serviceItem.getRemark());

                    // 查询脱敏规则
                    if (serviceItem.getDesensitizationRuleId() != null) {
                        RuleDesensitization ruleDesensitization = ruleDesensitizationMapper.selectRuleDesensitizationById(serviceItem.getDesensitizationRuleId());
                        if (ruleDesensitization != null) {
                            RedisDesensitizationRuleDto redisDesensitizationRuleDto = new RedisDesensitizationRuleDto();
                            redisDesensitizationRuleDto.setReqStr(ruleDesensitization.getRegularExpr());
                            redisDesensitizationRuleDto.setFillStr(ruleDesensitization.getReplaceExpr());
                            redisServiceParamDto.setDesensitizationRule(redisDesensitizationRuleDto);
                        }
                    }

                    serviceParamDtos.add(redisServiceParamDto);
                }
            }

            RedisServiceDataDto redisServiceDataDto = new RedisServiceDataDto();
            redisServiceDataDto.setServiceUrl(serviceShare.getServiceUrl());
            redisServiceDataDto.setAuthWay(serviceShare.getAuthWay());
            if (serviceShare.getNeedAuth() != null && serviceShare.getNeedAuth().equals(DxpConstants.YES)) {
                redisServiceDataDto.setNeedAuth(1);
            }

            redisServiceDataDto.setParamForm(serviceShare.getParamForm());
            redisServiceDataDto.setResponseForm(serviceShare.getResponseForm());
            redisServiceDataDto.setRequestMethod(serviceShare.getRequestMethod());
            redisServiceDataDto.setParams(serviceParamDtos);

            // 整合过滤参数和审核时过滤参数合并
            ServiceIntegration serviceIntegration = serviceIntegrationMapper.selectServiceIntegrationById(serviceShare.getServiceId());
            if (serviceIntegration != null) {
                if (StringUtils.isNotEmpty(userResource.getStatusRemark()) && StringUtils.isNotEmpty(serviceIntegration.getFilterJson())) {
                    // 合并
                    try {
                        JSONObject src = JSON.parseObject(userResource.getStatusRemark());
                        JSONObject target = JSON.parseObject(serviceIntegration.getFilterJson());
                        JSONObject res = jsonMerge(src, target);
                        redisServiceDataDto.setFixParams(res.toJSONString());
                    } catch (Exception e) {

                    }
                } else if (StringUtils.isNotEmpty(userResource.getStatusRemark())) {
                    redisServiceDataDto.setFixParams(userResource.getStatusRemark());
                } else if (StringUtils.isNotEmpty(serviceIntegration.getFilterJson())) {
                    redisServiceDataDto.setFixParams(serviceIntegration.getFilterJson());
                }
            } else {
                redisServiceDataDto.setFixParams(userResource.getStatusRemark());
            }

            // 写入redis
            RedisResourceDto<RedisServiceDataDto> redisServiceDataDtoRedisResourceDto = new RedisResourceDto<>();
            redisServiceDataDtoRedisResourceDto.setResData(redisServiceDataDto);
            redisServiceDataDtoRedisResourceDto.setResName(userResource.getResourceName());
            redisServiceDataDtoRedisResourceDto.setResType(DxpConstants.RESOURCE_TYPE_SERVICE);
            redisServiceDataDtoRedisResourceDto.setAccessId(userResource.getCallId());
            redisServiceDataDtoRedisResourceDto.setDayLimit(userResource.getDayLimit());
            redisServiceDataDtoRedisResourceDto.setFormat(userResource.getFormat());
            if (userResource.getIsSubscribe() != null && userResource.getIsSubscribe().equals(DxpConstants.YES)) {
                redisServiceDataDtoRedisResourceDto.setIsSubscribe(1);
            }
            redisServiceDataDtoRedisResourceDto.setSubscribeUrl(userResource.getSubscribeUrl());
            redisServiceDataDtoRedisResourceDto.setAddField(serviceShare.getAddField());
            redisServiceDataDtoRedisResourceDto.setPageField(serviceShare.getPageField());
            redisServiceDataDtoRedisResourceDto.setPageSizeField(serviceShare.getPageSizeField());
            redisServiceDataDtoRedisResourceDto.setSearchField(serviceShare.getSearchField());
            redisServiceDataDtoRedisResourceDto.setApplication(applicationDto);

            // 写入接入redis
            String accessId = DxpRedisKeyConstants.RESOURCE_ACCESS.replace("{0}", userResource.getCallId());
            redisCache.deleteObject(accessId);
            redisCache.setCacheObject(accessId, redisServiceDataDtoRedisResourceDto);
        } else if (userResource.getResourceType().equals(DxpConstants.RESOURCE_TYPE_DATA)) {
            // 查询数据
            DataShare dataShare = dataShareMapper.selectDataShareById(userResource.getResourceId());
            if (dataShare == null) {
                return;
            }

            // 组装参数
            List<RedisDataColumnDto> dataColumnDtos = new ArrayList<>();
            List<DataItemResourceDto> dataItems = JSON.parseArray(userResourceItem.getResourceItem(), DataItemResourceDto.class);
            if (dataItems != null && dataItems.size() > 0) {
                for (DataItemResourceDto dataItem : dataItems) {
                    RedisDataColumnDto redisDataColumnDto = new RedisDataColumnDto();
                    redisDataColumnDto.setName(dataItem.getColumnName());
                    redisDataColumnDto.setType(dataItem.getColumnType());
                    if (dataItem.getIsSearchable() != null && dataItem.getIsSearchable().equals(DxpConstants.YES)) {
                        redisDataColumnDto.setIsSearchable(1);
                    }

                    if (dataItem.getIsRequired() != null && dataItem.getIsRequired().equals(DxpConstants.YES)) {
                        redisDataColumnDto.setIsRequired(1);
                    }

                    redisDataColumnDto.setFilterOperator(dataItem.getFilterOperator());
                    redisDataColumnDto.setFilterValue(dataItem.getFilterValue());

                    // 查询脱敏规则
                    if (dataItem.getDesensitizationRuleId() != null) {
                        RuleDesensitization ruleDesensitization = ruleDesensitizationMapper.selectRuleDesensitizationById(dataItem.getDesensitizationRuleId());
                        if (ruleDesensitization != null) {
                            RedisDesensitizationRuleDto redisDesensitizationRuleDto = new RedisDesensitizationRuleDto();
                            redisDesensitizationRuleDto.setReqStr(ruleDesensitization.getRegularExpr());
                            redisDesensitizationRuleDto.setFillStr(ruleDesensitization.getReplaceExpr());
                            redisDataColumnDto.setDesensitizationRule(redisDesensitizationRuleDto);
                        }
                    }

                    dataColumnDtos.add(redisDataColumnDto);
                }
            }

            RedisDataDataDto redisDataDataDto = new RedisDataDataDto();
            redisDataDataDto.setDbId(dataShare.getDatabaseId());
            redisDataDataDto.setTbName(dataShare.getTableName());
            redisDataDataDto.setColnums(dataColumnDtos);

            RedisResourceDto<RedisDataDataDto> redisDataDataDtoRedisResourceDto = new RedisResourceDto<>();
            redisDataDataDtoRedisResourceDto.setResData(redisDataDataDto);
            redisDataDataDtoRedisResourceDto.setResName(userResource.getResourceName());
            redisDataDataDtoRedisResourceDto.setResType(DxpConstants.RESOURCE_TYPE_DATA);
            redisDataDataDtoRedisResourceDto.setAccessId(userResource.getCallId());
            redisDataDataDtoRedisResourceDto.setDayLimit(userResource.getDayLimit());
            redisDataDataDtoRedisResourceDto.setFormat(userResource.getFormat());
            if (userResource.getIsSubscribe() != null && userResource.getIsSubscribe().equals(DxpConstants.YES)) {
                redisDataDataDtoRedisResourceDto.setIsSubscribe(1);
            }
            redisDataDataDtoRedisResourceDto.setSubscribeUrl(userResource.getSubscribeUrl());
            redisDataDataDtoRedisResourceDto.setAddField(dataShare.getAddField());
            redisDataDataDtoRedisResourceDto.setApplication(applicationDto);

            // 写入接入redis
            String accessId = DxpRedisKeyConstants.RESOURCE_ACCESS.replace("{0}", userResource.getCallId());
            redisCache.deleteObject(accessId);
            redisCache.setCacheObject(accessId, redisDataDataDtoRedisResourceDto);
        }
    }

    @Override
    public void delToRedis(String callId) {
        // 写入接入redis
        String accessId = DxpRedisKeyConstants.RESOURCE_ACCESS.replace("{0}", callId);
        redisCache.deleteObject(accessId);
    }

    /**
     * 全部资源写入redis
     */
    @Override
    public void writeToRedis() {
        // 查询所有资源
        UserResource req = new UserResource();
        req.setStatus(DxpConstants.STATUS_VALID);
        List<UserResource> userResources = userResourceMapper.selectUserResourceList(req);

        if (userResources == null || userResources.size() == 0) {
            return;
        }
        UserResourceItem reqItem = new UserResourceItem();
        List<UserResourceItem> userResourceItems = userResourceItemMapper.selectUserResourceItemList(reqItem);

        for (UserResource userResource : userResources) {
            UserResourceItem userResourceItem = userResourceItems.stream().filter(i -> i.getUserResourceId().equals(userResource.getId())).findFirst().get();
            if (userResourceItem == null) {
                return;
            }

            this.writeToRedis(userResource, userResourceItem);
        }
    }

    @Override
    public void writeToSubscribeRedis() {
        // 查询所有资源
        UserResource req = new UserResource();
        req.setStatus(DxpConstants.STATUS_VALID);
        List<UserResource> userResources = userResourceMapper.selectUserResourceList(req);

        if (userResources == null || userResources.size() == 0) {
            return;
        }

        List<RedisResourceDto> dtos = new ArrayList<>();
        for (UserResource userResource : userResources) {
            if (StringUtils.isNotEmpty(userResource.getIsSubscribe()) && userResource.getIsSubscribe().equals(DxpConstants.YES)) {
                RedisResourceDto redisResourceDto = new RedisResourceDto();
                BeanUtils.copyProperties(userResource, redisResourceDto);
                redisResourceDto.setResName(userResource.getResourceName());
                redisResourceDto.setResType(userResource.getResourceType());
                String accessId = DxpRedisKeyConstants.RESOURCE_ACCESS.replace("{0}", userResource.getCallId());
                redisResourceDto.setAccessId(accessId);

                Application application = applicationMapper.selectApplicationById(userResource.getApplicationId());
                RedisApplicationDto applicationDto = new RedisApplicationDto();
                applicationDto.setAppId(application.getAppId());
                applicationDto.setAppSecret(application.getAppSecret());
                redisResourceDto.setApplication(applicationDto);

                if (userResource.getResourceType().equals(DxpConstants.RESOURCE_TYPE_SERVICE)) {
                    ServiceShare serviceShare = serviceShareMapper.selectServiceShareById(userResource.getResourceId());
                    if (serviceShare == null || StringUtils.isEmpty(serviceShare.getAddField()) || StringUtils.isEmpty(serviceShare.getPageField()) || StringUtils.isEmpty(serviceShare.getPageSizeField())) {
                        continue;
                    }
                    redisResourceDto.setAddField(serviceShare.getAddField());
                    redisResourceDto.setSearchField(serviceShare.getSearchField());
                    redisResourceDto.setPageSizeField(serviceShare.getPageSizeField());
                    redisResourceDto.setPageField(serviceShare.getPageField());
                } else if (userResource.getResourceType().equals(DxpConstants.RESOURCE_TYPE_DATA)) {
                    DataShare dataShare = dataShareMapper.selectDataShareById(userResource.getResourceId());
                    if (dataShare == null || StringUtils.isEmpty(dataShare.getAddField())) {
                        continue;
                    }
                    redisResourceDto.setAddField(dataShare.getAddField());
                }
                dtos.add(redisResourceDto);
            }
        }

        if (dtos != null && dtos.size() > 0) {
            String key = DxpRedisKeyConstants.RESOURCE_SUBSCRIBE;
            redisCache.deleteObject(key);
            redisCache.setCacheObject(key, dtos);
        }
    }


    private JSONObject jsonMerge(JSONObject source, JSONObject target) {
        // 覆盖目标JSON为空，直接返回覆盖源
        if (target == null) {
            return source;
        }

        for (String key : source.keySet()) {
            Object value = source.get(key);
            if (!target.containsKey(key)) {
                target.put(key, value);
            } else {
                if (value instanceof JSONObject) {
                    JSONObject valueJson = (JSONObject) value;
                    JSONObject targetValue = jsonMerge(valueJson, target.getJSONObject(key));
                    target.put(key, targetValue);
                } else if (value instanceof JSONArray) {
                    JSONArray valueArray = (JSONArray) value;
                    for (int i = 0; i < valueArray.size(); i++) {
                        JSONObject obj = (JSONObject) valueArray.get(i);
                        JSONObject targetValue = jsonMerge(obj, (JSONObject) target.getJSONArray(key).get(i));
                        target.getJSONArray(key).set(i, targetValue);
                    }
                } else {
                    target.put(key, value);
                }
            }
        }
        return target;
    }
}
