package com.skr.mdm.api.system.service.impl;

import cn.hutool.core.collection.ListUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.github.pagehelper.PageHelper;
import com.google.common.collect.Lists;
import com.skr.mdm.api.system.service.ClientUpgradeService;
import com.skr.mdm.bean.qo.BaseQueryQO;
import com.skr.mdm.bean.qo.StrategyIssuedQO;
import com.skr.mdm.bean.qo.StrategyUserQO;
import com.skr.mdm.bean.qo.system.ClientUpgradeAddUpdateQO;
import com.skr.mdm.bean.qo.system.ClientUpgradeRepushQO;
import com.skr.mdm.bean.so.client.ClientUpgradeSO;
import com.skr.mdm.bean.vo.app.AppPushStatisticsVO;
import com.skr.mdm.bean.vo.app.MdmApkInfoVO;
import com.skr.mdm.constant.MinIoMessageConstants;
import com.skr.mdm.dao.ClientUpgradeDao;
import com.skr.mdm.dao.ClientUpgradeUserDao;
import com.skr.mdm.dao.MdmStrategyDeptDao;
import com.skr.mdm.dto.LoginAdminDTO;
import com.skr.mdm.entity.ClientUpgradeDO;
import com.skr.mdm.entity.ClientUpgradeUserDO;
import com.skr.mdm.entity.MdmStrategyDeptDO;
import com.skr.mdm.enums.*;
import com.skr.mdm.exception.ServiceException;
import com.skr.mdm.handle.IssuedUserHandler;
import com.skr.mdm.handle.LogAdminHandler;
import com.skr.mdm.manager.DeviceManager;
import com.skr.mdm.manager.oss.MinIoManager;
import com.skr.mdm.result.ResponseResult;
import com.skr.mdm.util.ApkUtil;
import com.skr.mdm.util.MdmListUtil;
import com.skr.mdm.util.PageUtil;
import lombok.extern.slf4j.Slf4j;
import net.dongliu.apk.parser.bean.ApkMeta;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author nfq
 * @date 2023-02-13 16:42
 */
@Service
@Slf4j
public class ClientUpgradeServiceImpl implements ClientUpgradeService {

    private final String MODULE = "客户端升级";

    private final String MDM_CLIENT_PACKAGE = "com.android.online.devmdm";

    @Autowired
    private MinIoManager minIoManager;
    @Autowired
    private ClientUpgradeDao clientUpgradeDao;
    @Autowired
    private ClientUpgradeUserDao clientUpgradeUserDao;
    @Autowired
    private LogAdminHandler logAdminHandler;
    @Autowired
    private IssuedUserHandler issuedUserHandler;
    @Autowired
    private MdmStrategyDeptDao strategyDeptDao;
    @Autowired
    private DeviceManager deviceManager;

    private static MdmApkInfoVO getMdmApkInfoVO(MultipartFile file, ApkMeta apkInfo, String fileUrl) {
        double fileSize = file.getSize() / (double) (1024 * 1024);
        BigDecimal fileSizeDecimal = new BigDecimal(fileSize);

        MdmApkInfoVO apkInfoVO = MdmApkInfoVO.builder()
                .fileName(file.getOriginalFilename())
                .appName(StringUtils.replace(apkInfo.getName(), " ", ""))
                .appSign(ApkUtil.getApkSign(file))
                .fileSize(fileSizeDecimal.setScale(2, RoundingMode.HALF_UP).floatValue() + "M")
                .fileUrl(fileUrl)
                .build();
        BeanUtils.copyProperties(apkInfo, apkInfoVO);
        return apkInfoVO;
    }

    private static void checkFileExtName(MultipartFile file) {
        String extName = StringUtils.substringAfterLast(file.getOriginalFilename(), ".");
        if (!StringUtils.equals("apk", extName)) {
            throw new ServiceException("仅支持apk文件！");
        }
    }

    private static void checkApkIfo(ApkMeta apkInfo) {
        if (apkInfo == null) {
            throw new ServiceException(ResultCodeEnum.APP_ANALYSIS_FAILURE);
        }
    }

    @Override
    public ResponseResult listClientUpgrade(BaseQueryQO baseQueryQO, LoginAdminDTO loginAdminDTO) {
        PageHelper.startPage(baseQueryQO.getPageNum(), baseQueryQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(clientUpgradeDao.listClientUpgradeVO(loginAdminDTO.getCompanyId(),
                baseQueryQO.getSearchType(), baseQueryQO.getSearchValue())));
    }

    @Override
    public ResponseResult listClientUpgradeUser(StrategyUserQO strategyUserQO) {
        if (strategyUserQO.getPageNum().equals(0)) {
            return ResponseResult.success(clientUpgradeUserDao.listClientUpgradeUserVO(strategyUserQO.getStrategyId(),
                    strategyUserQO.getSearchType(), strategyUserQO.getSearchValue()));
        }
        PageHelper.startPage(strategyUserQO.getPageNum(), strategyUserQO.getPageSize());
        return ResponseResult.success(PageUtil.getPageResult(clientUpgradeUserDao.listClientUpgradeUserVO(
                strategyUserQO.getStrategyId(), strategyUserQO.getSearchType(), strategyUserQO.getSearchValue())));
    }

    @Override
    public ResponseResult statisticsClientUpgrade(Integer clientUpgradeId) {
        List<Integer> userStateList = clientUpgradeUserDao.listUserState(clientUpgradeId);
        if (CollectionUtils.isNotEmpty(userStateList)) {
            Integer waitDownloadCount = 0;
            Integer downloadingCount = 0;
            Integer downloadFailedCount = 0;
            Integer downloadSuccessCount = 0;
            Integer installSuccessCount = 0;
            Integer installFailedCount = 0;
            for (Integer state : userStateList) {
                if (state.equals(1)) {
                    waitDownloadCount++;
                }
                if (state.equals(2)) {
                    downloadingCount++;
                }
                if (state.equals(3)) {
                    downloadFailedCount++;
                }
                if (state.equals(4)) {
                    downloadSuccessCount++;
                }
                if (state.equals(5)) {
                    installSuccessCount++;
                }
                if (state.equals(6)) {
                    installFailedCount++;
                }
            }
            AppPushStatisticsVO appPushStatisticsVO = AppPushStatisticsVO.builder()
                    .userCount(userStateList.size())
                    .waitDownloadCount(waitDownloadCount)
                    .downloadingCount(downloadingCount)
                    .downloadFailedCount(downloadFailedCount)
                    .downloadSuccessCount(downloadSuccessCount)
                    .installSuccessCount(installSuccessCount)
                    .installFailedCount(installFailedCount)
                    .build();
            return ResponseResult.success(appPushStatisticsVO);
        }
        return ResponseResult.success();
    }

    @Override
    public ResponseResult analyticApp(MultipartFile file, LoginAdminDTO loginAdminDTO) {
        // 判断是否为apk文件
        checkFileExtName(file);
        // 解析apk文件
        ApkMeta apkInfo = ApkUtil.getApkInfo(file);
        // 删除图标本地缓存
        ApkUtil.deleteIconFile();
        checkApkIfo(apkInfo);
        // 仅支持MDM客户端安装包
        checkAppPackageName(apkInfo.getPackageName());
        // 检查版本重复
        checkVersionName(apkInfo.getVersionName(), loginAdminDTO.getCompanyId());
        // 上传应用到文件服务
        String minioObject = MinIoMessageConstants.CLIENT_UPGRADE_OBJECT +
                apkInfo.getName() + "-" +
                apkInfo.getPackageName() + "-" +
                apkInfo.getVersionName() + ".apk";
        String fileUrl = minIoManager
                .putObjectMinIo(loginAdminDTO.getCompanyId(), minioObject, file, true);
        // 文件大小
        MdmApkInfoVO apkInfoVO = getMdmApkInfoVO(file, apkInfo, fileUrl);
        return ResponseResult.success(apkInfoVO);
    }

    @Override
    public ResponseResult addUpgradeApp(ClientUpgradeAddUpdateQO clientUpgradeAddUpdateQO, LoginAdminDTO loginAdminDTO) {
        // 检查包名
        checkAppPackageName(clientUpgradeAddUpdateQO.getPackageName());
        // 检查版本重复
        checkVersionName(clientUpgradeAddUpdateQO.getVersionName(), loginAdminDTO.getCompanyId());
        // 保存
        ClientUpgradeDO clientUpgradeDO = ClientUpgradeDO.builder()
                .companyId(loginAdminDTO.getCompanyId())
                .isPause(0)
                .operator(loginAdminDTO.getAdminName())
                .build();
        BeanUtils.copyProperties(clientUpgradeAddUpdateQO, clientUpgradeDO);
        clientUpgradeDao.insert(clientUpgradeDO);
        // log
        String content = "添加客户端升级：" + clientUpgradeAddUpdateQO.getAppName().concat("(")
                .concat(clientUpgradeAddUpdateQO.getVersionName()).concat(")");
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    private void checkVersionName(String versionName, Integer companyId) {
        Long count = new LambdaQueryChainWrapper<>(clientUpgradeDao)
                .eq(ClientUpgradeDO::getCompanyId, companyId)
                .eq(ClientUpgradeDO::getVersionName, versionName)
                .count();
        if (count > 0) {
            throw new ServiceException("该版本已存在！");
        }
    }

    @Override
    public ResponseResult updateUpgradeApp(ClientUpgradeAddUpdateQO clientUpgradeAddUpdateQO, LoginAdminDTO loginAdminDTO) {
        new LambdaUpdateChainWrapper<>(clientUpgradeDao)
                .set(ClientUpgradeDO::getGlobalType, clientUpgradeAddUpdateQO.getGlobalType())
                .set(ClientUpgradeDO::getAppName, clientUpgradeAddUpdateQO.getAppName())
                .set(ClientUpgradeDO::getRemark, clientUpgradeAddUpdateQO.getRemark())
                .eq(ClientUpgradeDO::getId, clientUpgradeAddUpdateQO.getId())
                .update();
        // log
        String content = "编辑客户端升级：" + clientUpgradeAddUpdateQO.getAppName().concat("(")
                .concat(clientUpgradeAddUpdateQO.getVersionName()).concat(")");
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, null);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult deleteUpgradeApp(List<Integer> clientUpgradeIdList, LoginAdminDTO loginAdminDTO) {
        // 下发
        clientUpgradeIdList.forEach(clientUpgradeId -> {
            List<Integer> equipmentIdList = new LambdaQueryChainWrapper<>(clientUpgradeUserDao)
                    .eq(ClientUpgradeUserDO::getClientUpgradeId, clientUpgradeId)
                    .list()
                    .stream().map(ClientUpgradeUserDO::getEquipmentId)
                    .collect(Collectors.toList());
            issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                    ClientUpgradeCmdEnum.REMOVE_CLIENT_UPGRADE.getCmd(), JSONUtil.toJsonStr(ListUtil.toList(clientUpgradeId)));
            ClientUpgradeDO clientUpgradeDO = clientUpgradeDao.selectById(clientUpgradeId);
            // 文件服务删除
            minIoManager.removeObject(ListUtil.toList(clientUpgradeDO.getFileUrl()));
            // log
            String content = "删除客户端升级：" + clientUpgradeDO.getAppName().concat("(").concat(clientUpgradeDO.getVersionName()).concat(")");
            logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, equipmentIdList);
        });
        // 删除
        clientUpgradeUserDao.delete(new LambdaQueryWrapper<>(ClientUpgradeUserDO.class)
                .in(ClientUpgradeUserDO::getClientUpgradeId, clientUpgradeIdList));
        clientUpgradeDao.deleteBatchIds(clientUpgradeIdList);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult deleteUpgradeAppUser(Integer clientUpgradeId, List<Integer> equipmentIdList, LoginAdminDTO loginAdminDTO) {
        // 下发
        issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                ClientUpgradeCmdEnum.REMOVE_CLIENT_UPGRADE.getCmd(), JSONUtil.toJsonStr(ListUtil.toList(clientUpgradeId)));
        ClientUpgradeDO clientUpgradeDO = clientUpgradeDao.selectById(clientUpgradeId);
        // 删除
        clientUpgradeUserDao.delete(new LambdaQueryWrapper<>(ClientUpgradeUserDO.class)
                .eq(ClientUpgradeUserDO::getClientUpgradeId, clientUpgradeId)
                .in(ClientUpgradeUserDO::getEquipmentId, equipmentIdList));
        // log
        String content = "删除客户端升级作用人：" + clientUpgradeDO.getAppName().concat("(").concat(clientUpgradeDO.getVersionName()).concat(")");
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, equipmentIdList);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult pushUpgradeApp(StrategyIssuedQO strategyIssuedQO, LoginAdminDTO loginAdminDTO) {
        // 更新全局部门
        updateGlobalDept(strategyIssuedQO);
        // 更新操作人
        new LambdaUpdateChainWrapper<>(clientUpgradeDao)
                .set(ClientUpgradeDO::getOperator, loginAdminDTO.getAdminName())
                .in(ClientUpgradeDO::getId, strategyIssuedQO.getStrategyIdList())
                .update();
        // 保存
        List<Integer> equipmentIdList = deviceManager.getDeviceIdList(strategyIssuedQO.getGlobalType(),
                strategyIssuedQO.getCandidateIdList(), loginAdminDTO.getCompanyId());
        if (CollectionUtils.isEmpty(equipmentIdList)) {
            return ResponseResult.success();
        }
        List<ClientUpgradeSO> clientUpgradeSOList = Lists.newArrayListWithCapacity(strategyIssuedQO.getStrategyIdList().size());
        List<String> appNameList = Lists.newArrayListWithCapacity(strategyIssuedQO.getStrategyIdList().size());
        strategyIssuedQO.getStrategyIdList().forEach(clientUpgradeId -> {
            ClientUpgradeSO clientUpgradeSO = getClientUpgradeSO(clientUpgradeId);
            clientUpgradeSOList.add(clientUpgradeSO);
            if (CollectionUtils.isNotEmpty(equipmentIdList)) {
                List<Integer> listEquipmentIdByStrategyIds = clientUpgradeUserDao.listEquipmentIdByStrategyIds(ListUtil.toList(clientUpgradeId));
                List<Integer> addEquipmentIdList = MdmListUtil.difference(equipmentIdList, listEquipmentIdByStrategyIds);
                List<Integer> updateEquipmentIdList = MdmListUtil.intersection(equipmentIdList, listEquipmentIdByStrategyIds);
                // 保存
                saveClientUpgradeUser(clientUpgradeId, addEquipmentIdList, clientUpgradeSO.getIsPause());
                // 更新
                updateClientUpgradeUser(clientUpgradeId, updateEquipmentIdList);
            }
            // log
            appNameList.add(clientUpgradeSO.getAppName().concat("(").concat(clientUpgradeSO.getVersionName()).concat(")"));
        });
        // 下发
        if (CollectionUtils.isNotEmpty(clientUpgradeSOList)) {
            issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(),
                    ClientUpgradeCmdEnum.PUSH_CLIENT_UPGRADE.getCmd(), JSONUtil.toJsonStr(clientUpgradeSOList));
        }
        // log
        String content = "下发客户端升级：" + StringUtils.join(appNameList, "、");
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, equipmentIdList);
        return ResponseResult.success();
    }

    @Override
    public ResponseResult repushUpgradeApp(ClientUpgradeRepushQO clientUpgradeRepushQO, LoginAdminDTO loginAdminDTO) {
        // 下发
        ClientUpgradeSO clientUpgradeSO = getClientUpgradeSO(clientUpgradeRepushQO.getClientUpgradeId());
        issuedUserHandler.issuedUser(clientUpgradeRepushQO.getEquipmentIdList(), loginAdminDTO.getCompanyId(),
                ClientUpgradeCmdEnum.PUSH_CLIENT_UPGRADE.getCmd(), JSONUtil.toJsonStr(ListUtil.toList(clientUpgradeSO)));
        // 更新作用人状态
        new LambdaUpdateChainWrapper<>(clientUpgradeUserDao)
                .set(ClientUpgradeUserDO::getState, StrategyEquipmentStateEnum.ISSUING.getState())
                .set(ClientUpgradeUserDO::getIssuedTime, new Date())
                .eq(ClientUpgradeUserDO::getClientUpgradeId, clientUpgradeRepushQO.getClientUpgradeId())
                .in(ClientUpgradeUserDO::getEquipmentId, clientUpgradeRepushQO.getEquipmentIdList())
                .update();
        // log
        String content = "客户端升级重新推送：" + clientUpgradeSO.getAppName().concat("(").concat(clientUpgradeSO.getVersionName()).concat(")");
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, clientUpgradeRepushQO.getEquipmentIdList());
        return ResponseResult.success();
    }

    @Override
    public ResponseResult pauseUpgrade(Integer clientUpgradeId, Integer isPause, LoginAdminDTO loginAdminDTO) {
        // 更新
        new LambdaUpdateChainWrapper<>(clientUpgradeDao)
                .set(ClientUpgradeDO::getIsPause, isPause)
                .eq(ClientUpgradeDO::getId, clientUpgradeId)
                .update();
        new LambdaUpdateChainWrapper<>(clientUpgradeUserDao)
                .set(ClientUpgradeUserDO::getPauseStatus, isPause)
                .eq(ClientUpgradeUserDO::getClientUpgradeId, clientUpgradeId)
                .update();
        // 下发
        String pauseStr = "恢复客户端升级推送:";
        String cmdType = ClientUpgradeCmdEnum.RECOVER_CLIENT_UPGRADE.getCmd();
        if (isPause == 1) {
            cmdType = ClientUpgradeCmdEnum.PAUSE_CLIENT_UPGRADE.getCmd();
            pauseStr = "暂停客户端升级推送:";
        }
        List<Integer> equipmentIdList = clientUpgradeUserDao.listEquipmentIdByStrategyIds(ListUtil.toList(clientUpgradeId));
        issuedUserHandler.issuedUser(equipmentIdList, loginAdminDTO.getCompanyId(), cmdType, JSONUtil.toJsonStr(ListUtil.toList(clientUpgradeId)));
        // log
        ClientUpgradeDO clientUpgradeDO = clientUpgradeDao.selectById(clientUpgradeId);
        String content = pauseStr.concat(clientUpgradeDO.getAppName().concat("(").concat(clientUpgradeDO.getVersionName()).concat(")"));
        logAdminHandler.log(loginAdminDTO, LogAdminTypeEnum.OTHER_TYPE.getLogType(), content, MODULE, equipmentIdList);
        return ResponseResult.success();
    }

    private ClientUpgradeSO getClientUpgradeSO(Integer clientUpgradeId) {
        ClientUpgradeDO clientUpgradeDO = clientUpgradeDao.selectById(clientUpgradeId);
        ClientUpgradeSO clientUpgradeSO = ClientUpgradeSO.builder()
                .sendTime(new Date())
                .build();
        BeanUtils.copyProperties(clientUpgradeDO, clientUpgradeSO);
        return clientUpgradeSO;
    }

    private void updateClientUpgradeUser(Integer clientUpgradeId, List<Integer> deviceIdList) {
        if (CollectionUtils.isNotEmpty(deviceIdList)) {
            new LambdaUpdateChainWrapper<>(clientUpgradeUserDao)
                    .set(ClientUpgradeUserDO::getState, StrategyEquipmentStateEnum.ISSUING.getState())
                    .set(ClientUpgradeUserDO::getIssuedTime, new Date())
                    .in(ClientUpgradeUserDO::getEquipmentId, deviceIdList)
                    .eq(ClientUpgradeUserDO::getClientUpgradeId, clientUpgradeId)
                    .update();
        }
    }

    private void saveClientUpgradeUser(Integer clientUpgradeId, List<Integer> equipmentIds, Integer pauseStatus) {
        if (CollectionUtils.isNotEmpty(equipmentIds)) {
            List<ClientUpgradeUserDO> clientUpgradeUserDOList = equipmentIds.stream().map(equipmentId ->
                            ClientUpgradeUserDO.builder()
                                    .clientUpgradeId(clientUpgradeId)
                                    .equipmentId(equipmentId)
                                    .state(StrategyEquipmentStateEnum.ISSUING.getState())
                                    .pauseStatus(pauseStatus)
                                    .issuedTime(new Date())
                                    .build())
                    .collect(Collectors.toList());
            clientUpgradeUserDao.insertBatch(clientUpgradeUserDOList);
        }
    }

    private void updateGlobalDept(StrategyIssuedQO strategyIssuedQO) {
        if (IssuedTypeEnum.DEPT_GLOBAL.getIssuedType().equals(strategyIssuedQO.getGlobalType())) {
            // 保存全局部门
            List<Integer> deptIdList = Lists.newCopyOnWriteArrayList(strategyIssuedQO.getCandidateIdList());
            strategyIssuedQO.getStrategyIdList().forEach(clientUpgradeId -> {
                saveAppDept(deptIdList, clientUpgradeId);
            });
        }
    }

    private void saveAppDept(List<Integer> departmentIdList, Integer clientUpgradeId) {
        if (CollectionUtils.isNotEmpty(departmentIdList)) {
            // 去重
            List<Integer> alreadyDepartmentIdList = new LambdaQueryChainWrapper<>(strategyDeptDao)
                    .eq(MdmStrategyDeptDO::getStrategyId, clientUpgradeId)
                    .eq(MdmStrategyDeptDO::getStrategyType, StrategyDeptTypeEnum.CLIENT_UPGRADE.getStrategyType())
                    .list()
                    .stream().map(MdmStrategyDeptDO::getDepartmentId)
                    .collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(alreadyDepartmentIdList)) {
                departmentIdList.removeAll(alreadyDepartmentIdList);
            }
            if (CollectionUtils.isNotEmpty(departmentIdList)) {
                List<MdmStrategyDeptDO> strategyDeptDOList = departmentIdList
                        .stream()
                        .map(departmentId -> MdmStrategyDeptDO
                                .builder()
                                .strategyId(clientUpgradeId)
                                .strategyType(StrategyDeptTypeEnum.CLIENT_UPGRADE.getStrategyType())
                                .departmentId(departmentId)
                                .build())
                        .collect(Collectors.toList());
                strategyDeptDao.insertBatch(strategyDeptDOList);
            }
        }
    }

    private void checkAppPackageName(String packageName) {
        if (!StringUtils.equals(MDM_CLIENT_PACKAGE, packageName)) {
            throw new ServiceException("仅支持客户端安装包！");
        }
    }
}
