package com.hotent.portal.persistence.manager.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.hotent.base.cache.annotation.CacheEvict;
import com.hotent.base.cache.annotation.CachePut;
import com.hotent.base.cache.annotation.Cacheable;
import com.hotent.base.cache.annotation.FirstCache;
import com.hotent.base.constants.CacheKeyConst;
import com.hotent.base.datasource.DatabaseContext;
import com.hotent.base.datasource.DatabaseSwitchResult;
import com.hotent.base.exception.BaseException;
import com.hotent.base.feign.BpmRuntimeFeignService;
import com.hotent.base.manager.CommonManager;
import com.hotent.base.manager.impl.BaseManagerImpl;
import com.hotent.base.model.CommonResult;
import com.hotent.base.util.*;
import com.hotent.base.util.Base64;
import com.hotent.portal.model.Popup;
import com.hotent.portal.persistence.dao.PopupDao;
import com.hotent.portal.persistence.manager.PopupManager;
import com.hotent.sys.persistence.dao.SysAuthUserDao;
import com.hotent.sys.persistence.manager.CurrentUserService;
import com.hotent.sys.persistence.manager.SysAuthUserManager;
import com.hotent.sys.persistence.model.SysAuthUser;
import com.hotent.table.datasource.DataSourceUtil;
import com.hotent.uc.api.impl.util.ContextUtil;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.sql.DataSource;
import java.io.File;
import java.io.Serializable;
import java.nio.charset.StandardCharsets;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.Statement;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * 弹窗管理表 服务实现类
 *
 * @author zengst
 * @company 广州宏天软件股份有限公司
 * @since 2020-11-27
 */
@Service
public class PopupManagerImpl extends BaseManagerImpl<PopupDao, Popup> implements PopupManager {

    private static final String REPLACEMENT_RESULT = "{result}";

    private static final String REPLACEMENT_CUR_USER_ID = "{curUserId}";

    @Resource
    CurrentUserService currentUserService;

    @Resource
    SysAuthUserDao sysAuthUserDao;

    @Resource
    CommonManager commonManager;

    @Resource
    JdbcTemplate jdbcTemplate;

    @Resource
    DatabaseContext databaseContext;

    @Resource
    BpmRuntimeFeignService runtimeFeignService;

    @Resource
    SysAuthUserManager sysAuthUserManager;

    @Override
    @Cacheable(value = CacheKeyConst.POPUP_REMIND, key = "#userId", firstCache = @FirstCache(expireTime = 600))
    public List<Popup> authList(String userId) {
        LocalDateTime curDate = LocalDateTime.now();
        QueryWrapper<Popup> qw = new QueryWrapper<>();
        qw.eq("is_enable_", 1)
                .lt("VALIDITY_START_", curDate)
                .gt("VALIDITY_END_", curDate)
                .orderByDesc("ORDER_");
        List<Popup> portalSysPopups = baseMapper.selectList(qw);
        return this.filterAuth(portalSysPopups, userId);
    }


    @Override
    public boolean save(Popup entity) {
        return super.save(entity);

    }

    @Transactional
    @CacheEvict(value = CacheKeyConst.POPUP_REMIND, allEntries = true)
    public boolean saveWithAuth(Popup popup) throws Exception {
        Popup byAlias = this.getByAlias(popup.getAlias());
        if (BeanUtils.isNotEmpty(byAlias)) {
            throw new BaseException("别名【" + popup.getAlias() + "】已存在");
        }
        boolean result = this.save(popup);
        if (result) {
            //新建栏目，默认授权给所有人
            String ownerNameJson = " [{\"type\":\"everyone\",\"title\":\"所有人\",\"checked\":true}]";
            sysAuthUserManager.saveRights(popup.getId(), SysAuthUser.BPMDEFUSER_OBJ_TYPE.POPUP, ownerNameJson);
            return true;
        }
        return result;
    }

    @Override
    @CacheEvict(value = CacheKeyConst.POPUP_REMIND, allEntries = true)
    public boolean updateById(Popup entity) {
        return super.updateById(entity);
    }

    @Override
    @CacheEvict(value = CacheKeyConst.POPUP_REMIND, allEntries = true)
    public boolean removeById(Serializable id) {
        return super.removeById(id);
    }

    @Override
    public boolean removeByIds(Collection<? extends Serializable> idList) {
        return this.removeWithAuth(idList);
    }

    @Override
    @CacheEvict(value = CacheKeyConst.POPUP_REMIND, allEntries = true)
    public boolean removeWithAuth(Collection<? extends Serializable> idList) {
        //删除授权信息
        QueryWrapper<SysAuthUser> authQw = new QueryWrapper<>();
        authQw.eq("OBJ_TYPE_", SysAuthUser.BPMDEFUSER_OBJ_TYPE.POPUP)
                .in("AUTHORIZE_ID_", idList);
        sysAuthUserManager.remove(authQw);
        return super.removeByIds(idList);
    }

    //权限过滤
    @Override
    public List<Popup> filterAuth(List<Popup> portalSysPopups, String userId) {
        if (BeanUtils.isEmpty(portalSysPopups)) {
            return new ArrayList<>();
        }
        Map<String, Set<String>> userRightMap = currentUserService.getUserRightMap();
        //用户权限列表
        Map<String, String> userRightMapStr = currentUserService.getMapStringByMayList(userRightMap);
        List<Popup> filterResult = new ArrayList<>();
        for (Popup popup : portalSysPopups) {
            if (BeanUtils.isNotEmpty(sysAuthUserDao.getAuthByAuthorizeId(userRightMapStr, popup.getId()))) {
                filterResult.add(popup);
            }
        }
        return filterResult;
    }

    //通过sql过滤
    @Override
    public List<Popup> filterData(List<Popup> authList, String userId) throws Exception {
        if (BeanUtils.isEmpty(authList)) {
            return new ArrayList<>();
        }
        List<Popup> filterResult = new ArrayList<>();
        for (Popup popup : authList) {
            if (Popup.DATA_TYPE_SQL.equals(popup.getDataType())) {
                this.filterBySql(filterResult, popup, userId);
            } else if (Popup.DATA_TYPE_SCRIPT.equals(popup.getDataType())) {
                this.filterByScript(filterResult, popup, userId);
            }
        }
        return filterResult;
    }

    private void filterBySql(List<Popup> result, Popup popup, String userId) throws Exception {
        if (StringUtil.isEmpty(popup.getSql())) {
            return;
        }
        if (popup.getSql().contains(REPLACEMENT_CUR_USER_ID)) {
            popup.setSql(popup.getSql().replace(REPLACEMENT_CUR_USER_ID, userId));
        }
        try (DatabaseSwitchResult res = databaseContext.setDataSource(popup.getDataSourceAlias())) {
            List<String> query = jdbcTemplate.queryForList(popup.getSql(), String.class);
            if (BeanUtils.isEmpty(query) || 1 != query.size()) {
                return;
            }
            String count = query.get(0);
            if (BeanUtils.isEmpty(count) || 0 == Integer.parseInt(count)) {
                return;
            }
            if (StringUtil.isNotEmpty(popup.getDesc()) && popup.getDesc().contains(REPLACEMENT_RESULT)) {
                popup.setDesc(popup.getDesc().replace(REPLACEMENT_RESULT, query.get(0)));
            }
            result.add(popup);
        }
    }

    private void filterByScript(List<Popup> result, Popup popup, String userId) throws Exception {
        if (StringUtil.isEmpty(popup.getScript())) {
            return;
        }
        if (popup.getScript().contains(REPLACEMENT_CUR_USER_ID)) {
            popup.setScript(popup.getScript().replace(REPLACEMENT_CUR_USER_ID, userId));
        }
        CommonResult<String> executeResult = runtimeFeignService.executeScript(popup.getScript());
        if (BeanUtils.isEmpty(executeResult) || !executeResult.getState()) {
            return;
        }
        if (StringUtil.isNotEmpty(popup.getDesc()) && popup.getDesc().contains(REPLACEMENT_RESULT)) {
            popup.setDesc(popup.getDesc().replace(REPLACEMENT_RESULT, executeResult.getValue()));
        }
        result.add(popup);
    }

    @Override
    public String exportPopup(List<String> ids)throws Exception {
        if (BeanUtils.isEmpty(ids)) {
            return "";
        }
        List<Popup> popups = baseMapper.selectBatchIds(ids);
        if (BeanUtils.isEmpty(popups)) {
            return "";
        }
        return JsonUtil.toJson(popups);
    }

    @Override
    @Transactional
    public void importFile(String fileStr)throws Exception {
        try {
            List list = JsonUtil.toBean(fileStr, List.class);
            for (Object o : list) {
                Popup popup = JsonUtil.toBean(JsonUtil.toJson(o), Popup.class);
                if (StringUtil.isNotEmpty(popup.getId()) && BeanUtils.isNotEmpty(this.getById(popup.getId()))) {
                    this.updateById(popup);
                }else{
                    this.saveWithAuth(popup);
                }
            }
        }catch (Exception e){
            throw new BaseException(e.getMessage());
        }
    }

    @Override
    public Popup getByAlias(String alias) {
        QueryWrapper<Popup> qw = new QueryWrapper<>();
        qw.eq("ALIAS_", alias);
        return this.getOne(qw);
    }

    @Override
    public CommonResult<Integer> validateSql(String sql, String dsAlias) throws Exception {
        if (StringUtil.isEmpty(sql)) {
            return new CommonResult<>(false, "sql为空", 0);
        }
        if (StringUtil.isEmpty(dsAlias)) {
            return new CommonResult<>(false, "数据源为空", 0);
        }
        if ("LOCAL_".equals(dsAlias)) {
            dsAlias = "master";
        }
        String decodeSql = new String(Base64.decodeBase64(sql), StandardCharsets.UTF_8);
        if (decodeSql.contains("{curUserId}")) {
            decodeSql = decodeSql.replace("{curUserId}", ContextUtil.getCurrentUserId());
        }
        DataSource dataSourceByAlias = DataSourceUtil.getDataSourceByAlias(dsAlias);
        Connection connection = dataSourceByAlias.getConnection();
        Statement statement = connection.createStatement(ResultSet.TYPE_SCROLL_SENSITIVE, ResultSet.CONCUR_READ_ONLY);
        try {
            statement.setQueryTimeout(30);
            ResultSet resultSet = statement.executeQuery(decodeSql);
            resultSet.last();
            int row = resultSet.getRow();
            if (1 != row) {
                return new CommonResult<>(false, "通过sql查询结果行数不为1", 0);
            }
            int columnCount = resultSet.getMetaData().getColumnCount();
            if (1 != columnCount) {
                return new CommonResult<>(false, "通过sql查询结果列数不为1", 0);
            }
            resultSet.beforeFirst();
            resultSet.next();
            return new CommonResult<>(true, "查询成功", Integer.parseInt(resultSet.getString(1)));
        } catch (Exception e) {
            return new CommonResult<>(false, "查询失败:" + e.getMessage(), 0);
        } finally {
            statement.close();
            connection.close();
        }
    }

    @Override
    public CommonResult<String> importPopup(Optional<Boolean> confirmImport,  Optional<String> cacheFileId) throws Exception {
        CommonResult<String> message = new CommonResult<>("导入成功");
        if (confirmImport.orElse(false)) {
            String fileStr = AppUtil.getBean(getClass()).getImportFileFromCache(cacheFileId.get());
            if(StringUtil.isEmpty(fileStr)) {
                return new CommonResult<>("导入的文件已经过期，请重新导入。");
            }
            importFile(fileStr);
        }
        delImportFileFromCache(cacheFileId.orElse(""));
        return message;

    }

    @Override
    @CachePut(value = CacheKeyConst.EIP_POPUP_IMPORT_FILE, key="#fileId",
            firstCache = @FirstCache(expireTime = 1, timeUnit = TimeUnit.HOURS))
    public String putImportFileInCache(String fileId, String fileJson) {
        return fileJson;
    }

    @Override
    @Cacheable(value = CacheKeyConst.EIP_POPUP_IMPORT_FILE, key="#fileId",
            firstCache = @FirstCache(expireTime = 1, timeUnit = TimeUnit.HOURS))
    public String getImportFileFromCache(String fileId) {
        return null;
    }

    @Override
    @CacheEvict(value = CacheKeyConst.EIP_POPUP_IMPORT_FILE, key="#fileId")
    public void delImportFileFromCache(String fileId) {}

    @Override
    public CommonResult<String> importCheck(MultipartHttpServletRequest request, HttpServletResponse response) {
        MultipartFile file = request.getFile("file");
        if (BeanUtils.isEmpty(file)) {
            return new CommonResult<>(false, "文件为空");
        }
        String uzPath = "";
        CommonResult<String> message;
        try {
            String rootRealPath = (FileUtil.getIoTmpdir() + "/attachFiles/unZip/").replace("/", File.separator);
            FileUtil.createFolder(rootRealPath, true);
            String name = file.getOriginalFilename();
            String fileDir = StringUtil.substringBeforeLast(name, ".");
            ZipUtil.unZipFile(file, rootRealPath); // 解压文件
            uzPath = rootRealPath + File.separator + fileDir; // 解压后文件的真正路径

            String fileStr = FileUtil.readFile(uzPath + File.separator + "popup.json");
            List list = JsonUtil.toBean(fileStr, List.class);
            List<String> names = new ArrayList<>();

            for (Object o : list) {
                Popup popup = JsonUtil.toBean(JsonUtil.toJson(o), Popup.class);
                Popup oldPopup = super.getById(popup.getId());
                if (oldPopup != null) {
                    names.add(popup.getName());
                }
            }

            if (BeanUtils.isEmpty(names)) {
                importFile(fileStr);
                message = new CommonResult<>(true, "导入成功");
            } else {
                String cacheFileId = UniqueIdUtil.getSuid();
                AppUtil.getBean(getClass()).putImportFileInCache(cacheFileId, fileStr);
                message = new CommonResult<String>(false, "导入失败，弹窗【" + String.join("，", names) + "】在系统中已存在，是否继续为其新增版本？", cacheFileId);
            }

        } catch (Exception e) {
            message = new CommonResult<>(false, "导入失败：" + e.getMessage());
        } finally {
            if (StringUtil.isNotEmpty(uzPath)) {
                File file1 = new File(uzPath);
                if (file1.exists()) {
                    file1.delete();
                }
            }
        }
        return message;
    }


}
