package com.xinda.scada.service.impl;

import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.xinda.common.config.RuoYiConfig;
import com.xinda.common.constant.HttpStatus;
import com.xinda.common.core.domain.AjaxResult;
import com.xinda.common.core.domain.entity.SysRole;
import com.xinda.common.core.domain.entity.SysUser;
import com.xinda.common.core.domain.model.LoginUser;
import com.xinda.common.core.redis.RedisCache;
import com.xinda.common.exception.ServiceException;
import com.xinda.common.utils.DateUtils;
import com.xinda.common.utils.MessageUtils;
import com.xinda.common.utils.StringUtils;
import com.xinda.common.utils.bean.BeanUtils;
import com.xinda.common.utils.file.FileUploadUtils;
import com.xinda.iot.domain.DeviceLog;
import com.xinda.iot.domain.EventLog;
import com.xinda.iot.domain.FunctionLog;
import com.xinda.iot.model.HistoryModel;
import com.xinda.iot.service.IDeviceLogService;
import com.xinda.scada.convert.ScadaConvert;
import com.xinda.scada.domain.Scada;
import com.xinda.scada.domain.ScadaDeviceBind;
import com.xinda.scada.domain.ScadaGallery;
import com.xinda.scada.enums.ScadaTypeEnum;
import com.xinda.scada.mapper.ScadaDeviceBindMapper;
import com.xinda.scada.mapper.ScadaGalleryMapper;
import com.xinda.scada.mapper.ScadaMapper;
import com.xinda.scada.service.IScadaService;
import com.xinda.scada.vo.*;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.stream.Collectors;

import static com.xinda.common.utils.SecurityUtils.getLoginUser;

/**
 * 组态中心Service业务层处理
 *
 * @author kerwincui
 * @date 2023-11-10
 */
@Service
public class ScadaServiceImpl implements IScadaService
{
    @Resource
    private ScadaMapper scadaMapper;
    @Resource
    private ScadaGalleryMapper scadaGalleryMapper;
    @Resource
    private RedisCache redisCache;
    @Resource
    private ScadaDeviceBindMapper scadaDeviceBindMapper;
    @Resource
    private IDeviceLogService deviceLogService;

    /**
     * 查询组态中心
     *
     * @param id 组态中心主键
     * @return 组态中心
     */
    @Override
    public ScadaVO selectScadaById(Long id)
    {
        Scada scada = scadaMapper.selectScadaById(id);
        if (Objects.isNull(scada)) {
            return null;
        }
        ScadaVO scadaVO = ScadaConvert.INSTANCE.convertScadaVO(scada);
        if (ScadaTypeEnum.PRODUCT_TEMPLATE.getType().equals(scada.getType())) {
            Long productId = scadaMapper.selectProductByGuid(scada.getGuid());
            scadaVO.setProductId(productId);
        } else if (ScadaTypeEnum.SCENE_MODEL.getType().equals(scada.getType())) {
            Long sceneModelId = scadaMapper.selectSceneModelByGuid(scada.getGuid());
            scadaVO.setSceneModelId(sceneModelId);
        }
        return scadaVO;
    }

    /**
     * 查询组态中心列表
     *
     * @param scada 组态中心
     * @return 组态中心
     */
    @Override
    public List<Scada> selectScadaList(Scada scada)
    {
        // 多租户版本使用
        SysUser user = getLoginUser().getUser();
        scada.setDeptId(user.getDeptId());
        return scadaMapper.selectScadaList(scada);
    }

    /**
     * 新增组态中心
     *
     * @param scadaVO 组态中心
     * @return 结果
     */
    @Override
    public AjaxResult insertScada(ScadaVO scadaVO)
    {
//        scadaVO.setPageName("综合测试GKB-29813组态");
//        scadaVO.setProductId(219L);
//        scadaVO.setType(1);
        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return AjaxResult.error(MessageUtils.message("scada.please.login"));
        }
        if (ScadaTypeEnum.PRODUCT_TEMPLATE.getType().equals(scadaVO.getType())) {
            String guid = scadaMapper.selectGuidByProductId(scadaVO.getProductId());
            if (StringUtils.isNotEmpty(guid)){
                return AjaxResult.warn(MessageUtils.message("scada.product.has.relate.please.select.again"));
            }
        }
        if (ScadaTypeEnum.SCENE_MODEL.getType().equals(scadaVO.getType())) {
            String guid = scadaMapper.selectGuidBySceneModelId(scadaVO.getSceneModelId());
            if (StringUtils.isNotEmpty(guid)){
                return AjaxResult.warn(MessageUtils.message("scada.scene.has.relate.please.select.again"));
            }
        }
        // 多租户版本使用
        SysUser user = getLoginUser().getUser();
        if (null != user.getDeptId()) {
            scadaVO.setTenantId(user.getDept().getDeptUserId());
            scadaVO.setTenantName(user.getDept().getDeptUserName());
        } else {
            scadaVO.setTenantId(user.getUserId());
            scadaVO.setTenantName(user.getUserName());
        }

        scadaVO.setCreateBy(loginUser.getUserId().toString());
        UUID uuid = UUID.randomUUID();
        scadaVO.setGuid(uuid.toString());
        Scada scada = ScadaConvert.INSTANCE.convertScada(scadaVO);
        scada.setDeptId(user.getDept().getDeptId());
        int result = scadaMapper.insertScada(scada);
        if (result >0){
            if (ScadaTypeEnum.PRODUCT_TEMPLATE.getType().equals(scadaVO.getType())) {
                scadaMapper.updateProductGuid(scadaVO.getProductId(), uuid.toString());
            }
            if (ScadaTypeEnum.SCENE_MODEL.getType().equals(scadaVO.getType())) {
                scadaMapper.updateDeviceGuid(scadaVO.getSerialNumber(), uuid.toString());
            }
            return AjaxResult.success(uuid.toString());
        }else {
            return AjaxResult.error();
        }
    }

    @Override
    public AjaxResult insertScada(Scada scada){

        LoginUser loginUser = getLoginUser();
        if (loginUser == null) {
            return AjaxResult.error(MessageUtils.message("scada.please.login"));
        }
        // 多租户版本使用
        SysUser user = getLoginUser().getUser();
        if (null != user.getDeptId()) {
            scada.setTenantId(user.getDept().getDeptUserId());
            scada.setTenantName(user.getDept().getDeptUserName());
        } else {
            scada.setTenantId(user.getUserId());
            scada.setTenantName(user.getUserName());
        }
        scada.setCreateBy(loginUser.getUserId().toString());
        int result = scadaMapper.insertScada(scada);
        if (result >0){
            return AjaxResult.success(scada.getGuid());
        }else {
            return AjaxResult.error();
        }
    }

    /**
     * 查询组态页面列表
     *
     * @param scada 组态页面
     * @return 组态页面
     */
    @Override
    public List<ScadaVO> listScadaVO(Scada scada) {
        List<Scada> scadaList = scadaMapper.selectScadaList(scada);
        return ScadaConvert.INSTANCE.convertScadaVOList(scadaList);
    }


    /**
     * 修改组态中心
     *
     * @param scadaVO 组态中心
     * @return 结果
     */
    @Override
    public int updateScada(ScadaVO scadaVO)
    {
        if (ScadaTypeEnum.PRODUCT_TEMPLATE.getType().equals(scadaVO.getType()) && null != scadaVO.getProductId()) {
            String guid = scadaMapper.selectGuidByProductId(scadaVO.getProductId());
            if (StringUtils.isNotEmpty(guid) && !guid.equals(scadaVO.getGuid())){
                throw new ServiceException(MessageUtils.message("scada.product.has.relate.please.select.again"), HttpStatus.WARN);
            }
            if (StringUtils.isEmpty(guid)) {
                scadaMapper.deleteProductByGuids(Collections.singletonList(scadaVO.getGuid()));
                scadaMapper.updateProductGuid(scadaVO.getProductId(), scadaVO.getGuid());
            }
        }
        if (ScadaTypeEnum.SCENE_MODEL.getType().equals(scadaVO.getType()) && null != scadaVO.getSceneModelId()) {
            String guid = scadaMapper.selectGuidBySceneModelId(scadaVO.getSceneModelId());
            if (StringUtils.isNotEmpty(guid) && !guid.equals(scadaVO.getGuid())){
                throw new ServiceException(MessageUtils.message("scada.scene.has.relate.please.select.again"), HttpStatus.WARN);
            }
            if (StringUtils.isEmpty(guid)) {
                scadaMapper.deleteSceneModelByGuids(Collections.singletonList(scadaVO.getGuid()));
                scadaMapper.updateSceneModelGuid(scadaVO.getSceneModelId(), scadaVO.getGuid());
            }
        }
        Scada scada = ScadaConvert.INSTANCE.convertScada(scadaVO);
        return scadaMapper.updateScada(scada);
    }

    /**
     * 批量删除组态中心
     *
     * @param ids 需要删除的组态中心主键
     * @return 结果
     */
    @Override
    public int deleteScadaByIds(Long[] ids)
    {
        return scadaMapper.deleteScadaByIds(ids);
    }

    /**
     * 删除组态中心信息
     *
     * @param id 组态中心主键
     * @return 结果
     */
    @Override
    public int deleteScadaById(Long id)
    {
        return scadaMapper.deleteScadaById(id);
    }

    @Override
    public int deleteScadaByGuid(String guid) {
        return scadaMapper.deleteScadaByGuid(guid);
    }

    /**
     * 根据guid获取组态详情
     * @param guid 组态id
     * @return
     */
    @Override
    public Scada selectScadaByGuid(String guid) {
        Scada scada = scadaMapper.selectScadaByGuid(guid);
        // 查询绑定设备
        List<ScadaBindDeviceSimVO> simVOList = scadaDeviceBindMapper.listDeviceSimByGuid(scada.getGuid());
        scada.setBindDeviceList(simVOList);
        return scada;
    }

    @Override
    public AjaxResult uploadGalleryFavorites(MultipartFile file, String categoryName) {
        LoginUser loginUser = getLoginUser();
        if (Objects.isNull(loginUser)) {
            return AjaxResult.error("请登录后重试！");
        }
        // 上传文件路径
        String filePath;
        // 上传并返回新文件名称
        try{
            filePath = FileUploadUtils.upload(RuoYiConfig.getUploadPath(), file);
        }catch (Exception e){
            return AjaxResult.error(500,"上传图库文件异常，"+ e);
        }
        String fileName = file.getOriginalFilename();
        ScadaGallery scadaGallery = new ScadaGallery();
        scadaGallery.setFileName(fileName);
        scadaGallery.setCategoryName(categoryName);
        scadaGallery.setResourceUrl(filePath);
        Long userId = loginUser.getUserId();
        scadaGallery.setTenantId(userId);
        scadaGallery.setTenantName(loginUser.getUsername());
        int i = scadaGalleryMapper.insertScadaGallery(scadaGallery);
        if (i <= 0) {
            return AjaxResult.error("上传失败，请重试！");
        }
        String key = this.getGalleryFavoritesRedisKey(userId);
        redisCache.sAdd(key, scadaGallery.getId());
        return AjaxResult.success();
    }

    private String getGalleryFavoritesRedisKey(Long userId) {
        return "scada:gallery_favorites_userId_" + userId;
    }

    @Override
    public AjaxResult saveGalleryFavorites(FavoritesVO favoritesVO) {
        LoginUser loginUser = getLoginUser();
        if (Objects.isNull(loginUser)) {
            return AjaxResult.error("请登录后重试！");
        }
        Long userId = loginUser.getUserId();
        List<String> idList = StringUtils.str2List(favoritesVO.getIdStr(), ",", true, true);
        String key = this.getGalleryFavoritesRedisKey(userId);
        for (String id : idList) {
            redisCache.sAdd(key, id);
        }
        return AjaxResult.success();
    }

    @Override
    public AjaxResult deleteGalleryFavorites(Long[] ids) {
        LoginUser loginUser = getLoginUser();
        if (Objects.isNull(loginUser)) {
            return AjaxResult.error("请登录后重试！");
        }
        Long userId = loginUser.getUserId();
        String key = this.getGalleryFavoritesRedisKey(userId);
        for (Long id : ids) {
            redisCache.setRemove(key, id);
        }
        // 删除图库
        scadaGalleryMapper.deleteScadaGalleryByIds(ids);
        return AjaxResult.success();
    }

    @Override
    public List<ScadaGallery> listGalleryFavorites(ScadaGallery scadaGallery) {
        LoginUser loginUser = getLoginUser();
        if (Objects.isNull(loginUser)) {
            return new ArrayList<>();
        }
        Long userId = loginUser.getUserId();
        String key = this.getGalleryFavoritesRedisKey(userId);
        Set<Long> cacheSet = redisCache.getCacheSet(key);
        if (CollectionUtils.isEmpty(cacheSet)) {
            return new ArrayList<>();
        }
        return scadaGalleryMapper.selectScadaGalleryByIdSet(cacheSet);
    }

    @Override
    public Map<String, List<ScadaHistoryModelVO>> listThingsModelHistory(ThingsModelHistoryParam param) {
        String serialNumber = param.getSerialNumber();
        List<ThingsModelHistoryParam.ThingsModelSim> thingsModelList = param.getThingsModelList();
        if (CollectionUtils.isEmpty(thingsModelList)) {
            return new HashMap<>(2);
        }
        List<ScadaHistoryModelVO> historyModelList = new ArrayList<>();
        List<String> propertyIdentifierList = thingsModelList.stream().filter(t -> 1 == t.getType()).map(ThingsModelHistoryParam.ThingsModelSim::getIdentifier).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(propertyIdentifierList)) {
            DeviceLog deviceLog = new DeviceLog();
            deviceLog.setIdentityList(propertyIdentifierList);
            deviceLog.setSerialNumber(serialNumber);
            deviceLog.setBeginTime(param.getBeginTime());
            deviceLog.setEndTime(param.getEndTime());
            List<HistoryModel> historyModelList1 = deviceLogService.listHistory(deviceLog);
            for (HistoryModel historyModel : historyModelList1) {
                ScadaHistoryModelVO scadaHistoryModelVO = new ScadaHistoryModelVO();
                BeanUtils.copyProperties(historyModel, scadaHistoryModelVO);
                historyModelList.add(scadaHistoryModelVO);
            }
        }
        List<String> functionIdentifierList = thingsModelList.stream().filter(t -> 2 == t.getType()).map(ThingsModelHistoryParam.ThingsModelSim::getIdentifier).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(functionIdentifierList)) {
            FunctionLog functionLog = new FunctionLog();
            functionLog.setIdentifyList(functionIdentifierList);
            functionLog.setSerialNumber(serialNumber);
            functionLog.setBeginTime(DateUtils.dateTime(DateUtils.YY_MM_DD_HH_MM_SS, param.getBeginTime()));
            functionLog.setEndTime(DateUtils.dateTime(DateUtils.YY_MM_DD_HH_MM_SS, param.getEndTime()));
            historyModelList.addAll(scadaMapper.listFunctionLogHistory(functionLog));
        }
        List<String> eventIdentifierList = thingsModelList.stream().filter(t -> 3 == t.getType()).map(ThingsModelHistoryParam.ThingsModelSim::getIdentifier).collect(Collectors.toList());
        if (CollectionUtils.isNotEmpty(eventIdentifierList)) {
            EventLog eventLog = new EventLog();
            eventLog.setIdentityList(eventIdentifierList);
            eventLog.setSerialNumber(serialNumber);
            Map<String, Object> params = new HashMap<>(2);
            params.put("beginTime", param.getBeginTime());
            params.put("endTime",param.getEndTime());
            eventLog.setParams(params);
            historyModelList.addAll(scadaMapper.listEventLogHistory(eventLog));
        }
        // 分组
        return historyModelList.stream().collect(Collectors.groupingBy(ScadaHistoryModelVO::getIdentity));
    }

    @Override
    public Integer getStatusBySerialNumber(String serialNumber) {
        return scadaMapper.getStatusBySerialNumber(serialNumber);
    }

    @Override
    public ScadaStatisticVO selectStatistic() {
        Long tenantId = null;
        Long userId = null;
        SysUser user = getLoginUser().getUser();
        List<SysRole> roles = user.getRoles();
        for (int i = 0; i < roles.size(); i++) {
            if (roles.get(i).getRoleKey().equals("tenant")) {
                // 租户查看产品下所有设备
                tenantId = user.getUserId();
            } else if (roles.get(i).getRoleKey().equals("general")) {
                // 用户查看自己设备
                userId = user.getUserId();
            }
        }
        // 获取设备、产品和告警数量
        ScadaStatisticVO statistic = scadaMapper.selectDeviceProductAlertCount(tenantId, userId);
        if (statistic == null) {
            statistic = new ScadaStatisticVO();
            return statistic;
        }
        return statistic;
    }

    @Override
    public AjaxResult importJson(MultipartFile file, String guid)  throws IOException {
        InputStream inputStream = file.getInputStream();
        if(file.isEmpty()){
            return AjaxResult.error(MessageUtils.message("scada.invalid.profile"));
        }
        if(file.getOriginalFilename().indexOf("json")==-1){
            return AjaxResult.error(MessageUtils.message("scada.invalid.profile"));
        }
        Scada scada = new Scada();
        try {
            scada = JSON.parseObject(inputStream, Scada.class);
        }catch (Exception e){
            return AjaxResult.error(MessageUtils.message("scada.invalid.profile"));
        }finally {
            inputStream.close();
        }
        Scada oldScada = new Scada();
        if (StringUtils.isNotEmpty(guid)) {
            Scada queryScada = new Scada();
            queryScada.setGuid(guid);
            List<Scada> scadaList = scadaMapper.selectScadaList(queryScada);
            if (CollectionUtils.isNotEmpty(scadaList)) {
                oldScada = scadaList.get(0);
            }
        }
        String importGuid = scada.getGuid();
        Long tenantId = scada.getTenantId();
        oldScada.setScadaData(scada.getScadaData());
        int i = scadaMapper.updateScada(oldScada);
        if (i <= 0) {
            return AjaxResult.error(MessageUtils.message("import.fail"));
        }
        if (!tenantId.equals(oldScada.getTenantId())) {
            return AjaxResult.success(MessageUtils.message("scada.import.success.need.replace.variable"));
        }
        if (StringUtils.isNotEmpty(importGuid) && ScadaTypeEnum.PUBLIC.getType().equals(oldScada.getType())
                && ScadaTypeEnum.PUBLIC.getType().equals(scada.getType())) {
            List<String> guidList = Arrays.asList(guid, importGuid);
            List<ScadaDeviceBind> scadaDeviceBinds = scadaDeviceBindMapper.selectScadaDeviceBindListByIds(guidList);
            if (CollectionUtils.isNotEmpty(scadaDeviceBinds)) {
                List<String> serialNumbers = scadaDeviceBinds.stream().filter(d -> guid.equals(d.getScadaGuid())).map(ScadaDeviceBind::getSerialNumber).collect(Collectors.toCollection(ArrayList::new));
                List<String> importSerialNumbers = scadaDeviceBinds.stream().filter(d -> importGuid.equals(d.getScadaGuid())).map(ScadaDeviceBind::getSerialNumber).collect(Collectors.toCollection(ArrayList::new));
                importSerialNumbers.removeAll(serialNumbers);
                for (String importSerialNumber : importSerialNumbers) {
                    ScadaDeviceBind scadaDeviceBind = new ScadaDeviceBind();
                    scadaDeviceBind.setScadaGuid(guid);
                    scadaDeviceBind.setSerialNumber(importSerialNumber);
                    scadaDeviceBindMapper.insertScadaDeviceBind(scadaDeviceBind);
                }
            }
            return AjaxResult.success(MessageUtils.message("import.success"));
        }
        return AjaxResult.success(MessageUtils.message("scada.import.success.need.replace.variable"));
    }
}
