package com.microservices.zone.resource.service.impl;

import com.microservices.common.core.exception.ServiceException;
import com.microservices.common.core.utils.DateUtils;
import com.microservices.common.core.utils.StringUtils;
import com.microservices.common.datascope.annotation.DataScope;
import com.microservices.common.redis.service.RedisService;
import com.microservices.common.security.utils.SecurityUtils;
import com.microservices.system.api.RemoteFileService;
import com.microservices.system.api.domain.SysFileInfo;
import com.microservices.system.api.utils.FeignUtils;
import com.microservices.zone.common.service.IZoneAsyncService;
import com.microservices.zone.common.service.IZoneSortService;
import com.microservices.zone.detail.domain.ZoneDetail;
import com.microservices.zone.detail.service.IZoneDetailService;
import com.microservices.zone.resource.domain.ZoneResourceType;
import com.microservices.zone.resource.domain.vo.TargetResourceTypeNameVo;
import com.microservices.zone.resource.domain.vo.ZoneResourceFileVo;
import com.microservices.zone.resource.domain.vo.ZoneResourceTypeInputVo;
import com.microservices.zone.resource.domain.vo.ZoneResourceTypeUpdateVo;
import com.microservices.zone.resource.mapper.ZoneResourceTypeMapper;
import com.microservices.zone.resource.service.IZoneResourceService;
import com.microservices.zone.resource.service.IZoneResourceTypeService;
import com.microservices.zone.utils.ZoneConstants;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 特色专区资源类别Service业务层处理
 *
 * @author otto
 * @date 2023-06-27
 */
@Service
public class ZoneResourceTypeServiceImpl implements IZoneResourceTypeService {
    @Autowired
    private ZoneResourceTypeMapper zoneResourceTypeMapper;

    @Autowired
    @Lazy
    private IZoneResourceService zoneResourceService;
    @Autowired
    private IZoneDetailService zoneDetailService;
    @Autowired
    private IZoneSortService zoneSortService;
    @Autowired
    private RemoteFileService remoteFileService;
    @Autowired
    private RedisService redisService;
    @Autowired
    private IZoneAsyncService zoneAsyncService;

    /**
     * 查询特色专区资源类别
     *
     * @param id 特色专区资源类别主键
     * @return 特色专区资源类别
     */
    @Override
    public ZoneResourceType selectZoneResourceTypeById(Long id) {
        ZoneResourceType zoneResourceType = zoneResourceTypeMapper.selectZoneResourceTypeById(id);
        if (zoneResourceType == null) {
            throw new ServiceException("该资源类别不存在或无权限访问(资源类别id[" + id + "])");
        }
        zoneDetailService.selectZoneDetailById(zoneResourceType.getZoneId());
        return zoneResourceType;
    }

    /**
     * 查询特色专区资源类别列表
     *
     * @param zoneResourceType 特色专区资源类别
     * @param isOpen
     * @return 特色专区资源类别
     */
    @Override
    @DataScope(deptAlias = "zrt")
    public List<ZoneResourceType> selectZoneResourceTypeList(ZoneResourceType zoneResourceType, boolean isOpen) {
        if (isOpen) {
            return zoneResourceTypeMapper.selectContainFileZoneResourceTypeList(zoneResourceType);
        } else {
            return zoneResourceTypeMapper.selectZoneResourceTypeList(zoneResourceType);
        }
    }

    /**
     * 新增特色专区资源类别
     *
     * @param zoneResourceTypeInputVo 特色专区资源类别
     * @return 结果
     */
    @Override
    public int insertZoneResourceType(ZoneResourceTypeInputVo zoneResourceTypeInputVo) {
        ZoneDetail zoneDetail = zoneDetailService.selectZoneDetailById(zoneResourceTypeInputVo.getZoneId());
        checkNameInZone(zoneResourceTypeInputVo.getZoneId(), zoneResourceTypeInputVo.getName());
        checkFileTypeIsExistInZone(zoneResourceTypeInputVo.getZoneId(), zoneResourceTypeInputVo.getName(), zoneResourceTypeInputVo.getFileTypeList());
        ZoneResourceType zoneResourceType = zoneResourceTypeInputVo.toZoneResourceType();
        zoneResourceType.setCreateTime(DateUtils.getNowDate());
        zoneResourceType.setCreateBy(SecurityUtils.getUsername());
        zoneResourceType.setDeptId(zoneDetail.getDeptId());
        Integer maxSort = zoneSortService.getMaxSort(zoneResourceTypeInputVo.getZoneId(), ZoneResourceType.table_name);
        zoneResourceType.setSort(maxSort);
        return zoneResourceTypeMapper.insertZoneResourceType(zoneResourceType);
    }

    /**
     * 检查当前专区其他资源分类下是否已包含以下文件类型
     *
     * @param zoneId
     * @param currentTypeName
     * @param fileTypeListStr
     * @return
     */
    private void checkFileTypeIsExistInZone(Long zoneId, String currentTypeName, String fileTypeListStr) {
        if (StringUtils.isEmpty(currentTypeName)) {
            throw new ServiceException("资源类型名称不能为空！");
        }
        List<String> currentFileTypeList = stringToList(fileTypeListStr);
        // 获取专区下所有资源类别
        ZoneResourceType zoneResourceTypeSearch = new ZoneResourceType();
        zoneResourceTypeSearch.setZoneId(zoneId);
        List<ZoneResourceType> zoneResourceTypeList = selectZoneResourceTypeList(zoneResourceTypeSearch, false);
        for (ZoneResourceType zoneResourceType : zoneResourceTypeList) {
            if (!currentTypeName.equals(zoneResourceType.getName())) {
                List<String> fileTypeList = stringToList(zoneResourceType.getFileTypeList());
                List<String> isExistFileTypeList = fileTypeList.stream()
                        .filter(x -> currentFileTypeList.stream().anyMatch(x::equalsIgnoreCase))
                        .collect(Collectors.toList());
                if (!isExistFileTypeList.isEmpty()) {
                    throw new ServiceException("以下文件类型在资源类别[%s]中已存在(文件类型[%s])"
                            , zoneResourceType.getName()
                            , String.join(",", isExistFileTypeList));
                }
            }
        }
    }


    /**
     * 检查该特色专区下是否已存在该名称的资源类别
     *
     * @param zoneId
     * @param name
     */
    private void checkNameInZone(Long zoneId, String name) {
        ZoneResourceType oldZoneResourceType = zoneResourceTypeMapper.selectZoneResourceTypeByNameAndZoneId(name, zoneId);
        if (oldZoneResourceType != null) {
            throw new ServiceException("该特色专区下资源类别名称已存在(资源类别名称[" + name + "])");
        }
    }

    /**
     * 修改特色专区资源类别
     *
     * @param zoneResourceTypeUpdateVo 特色专区资源类别
     * @return 结果
     */
    @Override
    public int updateZoneResourceType(ZoneResourceTypeUpdateVo zoneResourceTypeUpdateVo) {
        ZoneResourceType oldZoneResourceType = selectZoneResourceTypeById(zoneResourceTypeUpdateVo.getId());
        boolean isChangeName = StringUtils.isNotEmpty(zoneResourceTypeUpdateVo.getName())
                && !oldZoneResourceType.getName().equals(zoneResourceTypeUpdateVo.getName());
        if (isChangeName) {
            // 修改资源类型名称时需清理该资源类型对应的所有缓存Key
            clearRedisSuffixKeyByZoneResourceType(oldZoneResourceType);
            checkNameInZone(oldZoneResourceType.getZoneId(), zoneResourceTypeUpdateVo.getName());
            if (ZoneConstants.RESOURCE_TYPE_OTHER_FILE.equals(oldZoneResourceType.getName())) {
                throw new ServiceException("【其他文件】为系统预留的资源类型，不允许修改名称");
            }
        }
        if (StringUtils.isNotEmpty(zoneResourceTypeUpdateVo.getFileTypeList())) {
            checkFileTypeIsExistInZone(oldZoneResourceType.getZoneId(), oldZoneResourceType.getName(), zoneResourceTypeUpdateVo.getFileTypeList());
        }

        ZoneResourceType zoneResourceType = zoneResourceTypeUpdateVo.toZoneResourceType();
        zoneResourceType.setUpdateTime(DateUtils.getNowDate());
        zoneResourceType.setUpdateBy(SecurityUtils.getUsername());
        if (zoneResourceTypeUpdateVo.getOrder() != null) {
            Integer setSort =
                    zoneSortService.setSortByOrder(oldZoneResourceType, zoneResourceTypeUpdateVo.getOrder(), ZoneResourceType.table_name);
            zoneResourceType.setSort(setSort);
        }
        int success = zoneResourceTypeMapper.updateZoneResourceType(zoneResourceType);
        if (isChangeName) {
            //防止该方法未执行完成就重新写入了redis Key，进行双删保证
            clearRedisSuffixKeyByZoneResourceType(oldZoneResourceType);
        }
        return success;
    }

    /**
     * 清理资源类型下所有文件类型缓存
     *
     * @param zoneResourceType
     */
    private void clearRedisSuffixKeyByZoneResourceType(ZoneResourceType zoneResourceType) {
        if (zoneResourceType != null && StringUtils.isNotBlank(zoneResourceType.getFileTypeList())) {
            List<String> fileSuffixList = stringToList(zoneResourceType.getFileTypeList());
            for (String fileSuffix : fileSuffixList) {
                redisService.deleteObject(
                        ZoneConstants.getFileSuffixOfResourceTypeRedisKey(zoneResourceType.getZoneId(), fileSuffix));
            }
        }
    }

    /**
     * 批量删除特色专区资源类别
     *
     * @param ids 需要删除的特色专区资源类别主键
     * @return 结果
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public int deleteZoneResourceTypeByIds(Long[] ids) {
        for (Long id : ids) {
            // 删除资源类别时需要将该资源类别下的所有文件类型移动到其他文件下
            ZoneResourceType zoneResourceType = selectZoneResourceTypeById(id);
            if (ZoneConstants.RESOURCE_TYPE_OTHER_FILE.equals(zoneResourceType.getName())) {
                throw new ServiceException("【其他文件】为系统预留的资源类型，不允许删除");
            }
            List<String> fileSuffixList = stringToList(zoneResourceType.getFileTypeList());
            for (String fileSuffix : fileSuffixList) {
                updateResourceTypeByFileSuffix(zoneResourceType.getZoneId(), ZoneConstants.RESOURCE_TYPE_OTHER_FILE, fileSuffix);
            }
        }
        return zoneResourceTypeMapper.deleteZoneResourceTypeByIds(ids);
    }

    @Override
    public ZoneResourceType getResourceTypeByFileId(Long zoneId, Long fileId) {
        SysFileInfo sysFileInfo = FeignUtils.getReturnData(
                remoteFileService.getFile(fileId)
        );
        if (sysFileInfo == null) {
            return null;
        }
        return getResourceTypeByFileSuffix(zoneId, sysFileInfo.getFileSuffix());
    }

    @Override
    public ZoneResourceType getResourceTypeByFileSuffix(Long zoneId, String fileSuffix) {
        ZoneResourceType zoneResourceType = redisService.getCacheObject(ZoneConstants.getFileSuffixOfResourceTypeRedisKey(zoneId, fileSuffix));
        if (zoneResourceType != null) {
            return zoneResourceType;
        }
        ZoneResourceType otherType = null;
        List<String> otherFileTypeList = null;
        // 获取专区下所有资源类别
        ZoneResourceType zoneResourceTypeSearch = new ZoneResourceType();
        zoneResourceTypeSearch.setZoneId(zoneId);
        List<ZoneResourceType> zoneResourceTypeList = selectZoneResourceTypeList(zoneResourceTypeSearch, false);
        // 检查该文件属于哪一个资源类别
        for (ZoneResourceType resourceType : zoneResourceTypeList) {
            List<String> fileTypeList = null;
            if (StringUtils.isNotEmpty(resourceType.getFileTypeList())) {
                fileTypeList = stringToList(resourceType.getFileTypeList());
                // 忽略大小写进行比对
                if (fileTypeList.stream().anyMatch(fileSuffix::equalsIgnoreCase)) {
                    zoneResourceType = resourceType;
                    break;
                }
            }
            // 获取其他文件资源类别数据
            if (ZoneConstants.RESOURCE_TYPE_OTHER_FILE.equals(resourceType.getName())) {
                otherType = resourceType;
                otherFileTypeList = fileTypeList;
            }
        }
        if (zoneResourceType == null) {
            // 当未在现有的资源类别中匹配到时，将该文件类型新增到其他文件中
            if (otherType == null) {
                ZoneResourceTypeInputVo zoneResourceTypeInputVo = new ZoneResourceTypeInputVo();
                zoneResourceTypeInputVo.setName(ZoneConstants.RESOURCE_TYPE_OTHER_FILE);
                zoneResourceTypeInputVo.setFileTypeList(fileSuffix);
                zoneResourceTypeInputVo.setZoneId(zoneId);
                insertZoneResourceType(zoneResourceTypeInputVo);
            } else {
                ZoneResourceTypeUpdateVo zoneResourceTypeUpdateVo = new ZoneResourceTypeUpdateVo();
                zoneResourceTypeUpdateVo.setId(otherType.getId());
                if (otherFileTypeList == null) {
                    otherFileTypeList = new ArrayList<>();
                }
                otherFileTypeList.add(fileSuffix);
                zoneResourceTypeUpdateVo.setFileTypeList(String.join(",", otherFileTypeList));
                updateZoneResourceType(zoneResourceTypeUpdateVo);
            }
            otherType = zoneResourceTypeMapper
                    .selectZoneResourceTypeByNameAndZoneId(ZoneConstants.RESOURCE_TYPE_OTHER_FILE, zoneId);
            zoneResourceType = otherType;
        }
        redisService.setCacheObject(ZoneConstants.getFileSuffixOfResourceTypeRedisKey(zoneId, fileSuffix), zoneResourceType);
        return zoneResourceType;

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateResourceTypeByFileId(TargetResourceTypeNameVo targetResourceTypeNameVo) {
        SysFileInfo sysFileInfo = FeignUtils.getReturnData(
                remoteFileService.getFile(targetResourceTypeNameVo.getFileId())
        );
        if (sysFileInfo == null) {
            return 1;
        }
        String fileSuffix = sysFileInfo.getFileSuffix();
        return updateResourceTypeByFileSuffix(targetResourceTypeNameVo.getZoneId(), targetResourceTypeNameVo.getName(), fileSuffix);
    }

    private List<String> stringToList(String listString) {
        if (StringUtils.isBlank(listString)) {
            return new ArrayList<>();
        }
        List<String> stringList = new ArrayList<>(Arrays.asList(listString.split(",")));
        stringList.removeIf(StringUtils::isBlank);
        return stringList;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int updateResourceTypeByFileSuffix(Long zoneId, String targetTypeName, String fileSuffix) {
        zoneDetailService.checkZoneDataScope(zoneId);
        ZoneResourceType currentResourceType = getResourceTypeByFileSuffix(zoneId, fileSuffix);
        // 当目前类型和目标类型一致时不进行切换操作
        if (currentResourceType.getName().equals(targetTypeName)) {
            return 1;
        }
        // 清理缓存数据
        redisService.deleteObject(ZoneConstants.getFileSuffixOfResourceTypeRedisKey(zoneId, fileSuffix));
        List<String> currentFileTypeList = stringToList(currentResourceType.getFileTypeList());
        // 当目标类型中不存在该文件类型时不进行变更
        if (currentFileTypeList.stream().anyMatch(fileSuffix::equalsIgnoreCase)) {
            currentResourceType.setUpdateBy(SecurityUtils.getUsername());
            currentResourceType.setUpdateTime(DateUtils.getNowDate());
            currentFileTypeList.remove(fileSuffix);
            currentResourceType.setFileTypeList(String.join(",", currentFileTypeList));
            zoneResourceTypeMapper.updateZoneResourceType(currentResourceType);
        }
        ZoneResourceType targetResourceType = zoneResourceTypeMapper.selectZoneResourceTypeByNameAndZoneId(
                targetTypeName, zoneId);
        if (targetResourceType == null) {
            ZoneResourceTypeInputVo zoneResourceTypeInputVo = new ZoneResourceTypeInputVo();
            zoneResourceTypeInputVo.setName(targetTypeName);
            zoneResourceTypeInputVo.setFileTypeList(fileSuffix);
            zoneResourceTypeInputVo.setZoneId(zoneId);
            insertZoneResourceType(zoneResourceTypeInputVo);
            targetResourceType = zoneResourceTypeMapper
                    .selectZoneResourceTypeByNameAndZoneId(targetTypeName, zoneId);
        } else {
            List<String> targetFileTypeList = stringToList(targetResourceType.getFileTypeList());
            // 当目标类型中存在该文件类型时不进行变更
            if (targetFileTypeList.stream().noneMatch(fileSuffix::equalsIgnoreCase)) {
                targetFileTypeList.add(fileSuffix);
                targetResourceType.setFileTypeList(String.join(",", targetFileTypeList));
                targetResourceType.setUpdateBy(SecurityUtils.getUsername());
                targetResourceType.setUpdateTime(DateUtils.getNowDate());
                zoneResourceTypeMapper.updateZoneResourceType(targetResourceType);
            }
        }
        // 清理缓存数据（双删）
        redisService.deleteObject(ZoneConstants.getFileSuffixOfResourceTypeRedisKey(zoneId, fileSuffix));
        // 异步更新该专区下当前资源类型对应的资源更新为目标资源类型
        zoneAsyncService.asyncUpdateZoneResourceToTargetType(currentResourceType, targetResourceType);
        return 1;
    }

    @Override
    public List<ZoneResourceType> selectZoneResourceTypeListBySuffixList(Long zoneId, List<String> fileSuffixList) {
        List<ZoneResourceType> zoneResourceTypeList = new ArrayList<>();
        for (String fileSuffix : fileSuffixList) {
            ZoneResourceType zoneResourceType = getResourceTypeByFileSuffix(zoneId, fileSuffix);
            if (zoneResourceTypeList.stream().noneMatch(x -> x.getId().equals(zoneResourceType.getId()))) {
                zoneResourceTypeList.add(zoneResourceType);
            }
        }
        return zoneResourceTypeList;
    }

    @Override
    public List<ZoneResourceType> selectZoneResourceTypeListInTypeIds(Long[] typeIds) {
        return zoneResourceTypeMapper.selectZoneResourceTypeListInTypeIds(typeIds);
    }

    @Override
    public List<ZoneResourceFileVo> getResourceTypeByFileIds(Long zoneId, String fileIds) {
        List<ZoneResourceFileVo> zoneResourceFileVoList = new ArrayList<>();
        List<SysFileInfo> fileInfoList = zoneResourceService.getAllFileByFileIds(fileIds);
        for (SysFileInfo fileInfo : fileInfoList) {
            ZoneResourceType zoneResourceType = getResourceTypeByFileSuffix(zoneId, fileInfo.getFileSuffix());
            ZoneResourceFileVo zoneResourceFileVo = new ZoneResourceFileVo(zoneResourceType, fileInfo);
            zoneResourceFileVoList.add(zoneResourceFileVo);
        }
        return zoneResourceFileVoList;
    }

}
