package com.boman.file.service;

import com.alibaba.fastjson.JSONObject;
import com.boman.common.core.utils.SecurityUtils;
import com.boman.common.core.utils.obj.ObjectUtils;
import com.boman.common.core.utils.poi.ExcelUtil;
import com.boman.common.redis.RedisKey;
import com.boman.common.redis.service.RedisService;
import com.boman.domain.GenTable;
import com.boman.domain.GenTableColumn;
import com.boman.domain.SysDept;
import com.boman.domain.VaccineInfoOperation;
import com.boman.domain.constant.CacheConstants;
import com.boman.domain.dto.AjaxResult;
import com.boman.domain.dto.ExportExcelDto;
import com.boman.domain.dto.FormDataDto;
import com.boman.domain.dto.ImportExcelDto;
import com.boman.domain.utils.ThreadPoolService;
import com.boman.file.utils.FileUploadUtils;
import com.boman.system.api.RemoteDeptService;
import com.boman.system.api.model.LoginUser;
import com.boman.web.core.api.RemoteAttendanceService;
import com.boman.web.core.api.RemoteObjService;
import com.boman.web.core.api.RemoteVaccineInfoService;
import org.apache.commons.lang3.BooleanUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;

import static com.boman.common.core.utils.obj.ObjectUtils.*;
import static com.boman.common.core.utils.poi.ExcelUtil.filterData;
import static com.boman.domain.constant.MaskConstant.LIST_VISIBLE;

/**
 * 本地文件存储
 * 
 * @author ruoyi
 */
@Primary
@Service
@RefreshScope
public class LocalSysFileServiceImpl implements ISysFileService
{

    private static final Logger LOGGER = LoggerFactory.getLogger(LocalSysFileServiceImpl.class);
    public static final String DOWNLOADING = "downloading";

    @Resource
    private RemoteObjService remoteObjService;
    @Resource
    private RedisService redisService;
    @Resource
    private RemoteAttendanceService remoteAttendanceService;
    @Resource
    private RemoteVaccineInfoService remoteVaccineInfoService;
    @Resource
    private RemoteDeptService remoteDeptService;

    /**
     * 资源映射路径 前缀
     */
    @Value("${file.prefix}")
    public String localFilePrefix;

    /**
     * 域名或本机访问地址
     */
    @Value("${file.domain}")
    public String domain;
    
    /**
     * 上传文件存储在本地的根路径
     */
    @Value("${file.path}")
    private String localFilePath;

    /******************************* 考勤表中的常量  *******************************/

    public static final String USERNAME= "userName";
    public static final String DEPT_NAME= "deptName";
    public static final String ATTENDANCE_TABLE_LEAVE_OR_SUM= "attendanceTableLeaveOrSum";
    public static final String ATTENDANCE_TABLE_LATE_SUM= "attendanceTableLateSum";
    public static final String ATTENDANCE_TABLE_LEAVE_SUM= "attendanceTableLeaveSum";
    public static final String DATE= "date";

    /**
     * 本地文件上传接口
     * 
     * @param file 上传的文件
     * @return 访问地址
     * @throws Exception
     */
    @Override
    public String uploadFile(MultipartFile file) throws Exception
    {
        String name = FileUploadUtils.upload(localFilePath, file);
        String url = domain + localFilePrefix + name;
        LOGGER.info("上传的路径为: {}", url);
        return url;
    }

    /**
     * 功能描述: 上传base64
     *
     * @param base64 base64
     * @return java.lang.String
     */
    @Override
    public String uploadFileBase64(String base64) throws IOException {
        MultipartFile multipartFile = FileUploadUtils.base64ToMultipart(base64);
        String name = FileUploadUtils.upload(localFilePath, multipartFile);
        String path = domain + localFilePrefix + name;
        LOGGER.info("上传的路径为: {}", path);
        return path;
    }

    /**
     * 功能描述: 通用的导入接口
     *
     * @param multipartFile multipartFile
     * @param tableName     tableName
     * @return java.util.List<com.alibaba.fastjson.JSONObject>
     */
    @Override
    public AjaxResult importExcelCommon(MultipartFile multipartFile, String tableName) throws Exception {
        Objects.requireNonNull(multipartFile, "multipartFile is empty");
        ObjectUtils.requireNonNull(tableName, "tableName is empty");

        GenTable genTable = redisService.getCacheObject(RedisKey.TABLE_INFO + tableName);
        List<GenTableColumn> columns = genTable.getColumns();

        ExcelUtil<JSONObject> util = new ExcelUtil<>(JSONObject.class);
        List<JSONObject> list = util.importCommonExcel("", multipartFile.getInputStream(), columns);

        ImportExcelDto dto = new ImportExcelDto();
        dto.setDataList(list);
        dto.setTableName(tableName);

        return remoteObjService.importCommonData(dto);
    }

    /**
     * 功能描述: 通用的导出接口
     *
     * @param response response
     * @param dto      tableName
     *                 empty(true=>只带表头的excel，不含数据, false=>带数据的excel)
     *                 condition 查询条件
     * @return com.boman.domain.dto.AjaxResult
     */
    @Override
    public AjaxResult exportExcelCommon(HttpServletResponse response, ExportExcelDto dto) {
        String tableName = dto.getTableName();
        Boolean empty = dto.getEmpty();
        ObjectUtils.requireNonNull(tableName, "exportExcelCommon tableName is empty");
        ObjectUtils.requireNonNull(empty, "exportExcelCommon empty is empty");

        GenTable genTable = redisService.getCacheObject(RedisKey.TABLE_INFO + tableName);
        List<GenTableColumn> columns = genTable.getColumns();
        columns = filterData(columns, 4, LIST_VISIBLE::equals);
        ExcelUtil<JSONObject> util = new ExcelUtil<>(JSONObject.class);
        List<Map<String, Object>> list;
        if (BooleanUtils.isTrue(empty)) {
            list = new ArrayList<>(0);
        } else {
            list = getData(dto, tableName, columns);
        }

        try {
            util.exportExcelCommon(response, list, "sheet1", columns, empty, false);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    /**
     * 功能描述: 通用的导出接口
     *
     * @param response response
     * @param dto      tableName
     *                 empty(true=>只带表头的excel，不含数据, false=>带数据的excel)
     *                 condition 查询条件
     * @return com.boman.domain.dto.AjaxResult
     */
    @Override
    public AjaxResult asyncExportExcelCommon(HttpServletRequest request, HttpServletResponse response, ExportExcelDto dto) {
        String tableName = dto.getTableName();
        Boolean empty = dto.getEmpty();
        ObjectUtils.requireNonNull(tableName, "exportExcelCommon tableName is empty");
        ObjectUtils.requireNonNull(empty, "exportExcelCommon empty is empty");

        GenTable genTable = redisService.getCacheObject(RedisKey.TABLE_INFO + tableName);
        List<GenTableColumn> allColumn = genTable.getColumns();
        List<GenTableColumn> columns = filterData(allColumn, 4, LIST_VISIBLE::equals);

        String filename = UUID.randomUUID() + ".xlsx";
        String fileAbsPath = localFilePath + "/" + filename;
        String fileStaticPath = domain + localFilePrefix + "/" + filename;
        File file = new File(fileAbsPath);

        ExcelUtil<JSONObject> util = new ExcelUtil<>(JSONObject.class);
        String username = SecurityUtils.getUsername();

        String token = SecurityUtils.getToken(request);
        LoginUser loginUser = redisService.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + token);
        Long deptId = loginUser.getSysUser().getDeptId();
        List<SysDept> sysDepts = remoteDeptService.listChildrenDepts(deptId);
        List<Long> deptIdList = new ArrayList<>();
        if (sysDepts != null && sysDepts.size() > 0) {
            for (SysDept sysDept : sysDepts) {
                Long id = sysDept.getId();
                deptIdList.add(id);
            }
        }

        ThreadPoolService.execute(() -> {
            List<Map<String, Object>> list;
            int size = 0;
            Boolean emptyIn = empty;
            if (BooleanUtils.isTrue(emptyIn)) {
                list = new ArrayList<>(0);
            } else {
                LOGGER.info("开始查询, 线程名称: {}", Thread.currentThread().getName());
                long currentTimeMillis = System.currentTimeMillis();
                list = getData1(dto, tableName, columns, token);
                if (isEmpty(list)) {
                    size = 0;
                    emptyIn = true;
                } else {
                    size = list.size();
                }

                LOGGER.info("查询到的数据长度为: {}, 查询耗时：{}秒, 文件：{}", size, (System.currentTimeMillis() - currentTimeMillis) / 1000, filename);
            }

            try {
                util.asyncExportExcelCommon(new FileOutputStream(file), list, "sheet1", columns, emptyIn, false);
//                long timeout = 2L;
//                if (size > 100000 && size < 500000) {
//                    timeout = 5L;
//                } else if(size > 500000){
//                    timeout = 10L;
//                }

                String key = RedisKey.ASYNC_DOWNLOAD_YMJZ + username;
                saveIntoRedis(fileAbsPath, fileStaticPath, key);
            } catch (IOException e) {
                LOGGER.error("导出失败", e);
                e.printStackTrace();
            }
        });

        return AjaxResult.success("成功", fileStaticPath);
    }



    /**
     * 功能描述: 通用的导出接口
     *
     * @param response response
     * @param info      查询条件
     * @return com.boman.domain.dto.AjaxResult
     */
    @Override
    public AjaxResult asyncExportYmjzExcel(HttpServletResponse response, VaccineInfoOperation info) throws IOException {
        GenTable genTable = redisService.getCacheObject(RedisKey.TABLE_INFO + "vaccine_info");
        List<GenTableColumn> columns = filterData(genTable.getColumns(), 4, LIST_VISIBLE::equals);

        String filename = UUID.randomUUID() + ".xlsx";
        String fileAbsPath = localFilePath + "/" + filename;
        String fileStaticPath = domain + localFilePrefix + "/" + filename;

        asyncHandle(info, columns, fileAbsPath, fileStaticPath);
        return AjaxResult.success("成功", fileStaticPath);
    }

    private void asyncHandle(VaccineInfoOperation info, List<GenTableColumn> columns, String fileAbsPath, String fileStaticPath) {
        final String username = SecurityUtils.getUsername();
        final ExcelUtil<JSONObject> util = new ExcelUtil<>(JSONObject.class);
        final File file = new File(fileAbsPath);

        ThreadPoolService.execute(() -> {
            List<Map<String, Object>> list;
            LOGGER.info("开始查询, 线程名称: {}", Thread.currentThread().getName());
            long currentTimeMillis = System.currentTimeMillis();

            String key = RedisKey.ASYNC_DOWNLOAD_YMJZ + username;
            saveIntoRedis(fileAbsPath, fileStaticPath, key);

            list = listYmjz(info, columns);
            boolean empty = isEmpty(list);
            int size = BooleanUtils.isFalse(empty) ? 0 : list.size();
            LOGGER.info("查询到的数据长度为: {}, 查询耗时：{}秒", size, (System.currentTimeMillis() - currentTimeMillis) / 1000);

            try {
                util.asyncExportExcelCommon(new FileOutputStream(file), list, "sheet1", columns, empty, false);
            } catch (IOException e) {
                LOGGER.error("导出失败", e);
                redisService.deleteObject(key);
                e.printStackTrace();
            }
        });
    }

    private void saveIntoRedis(String fileAbsPath, String fileStaticPath, String key) {
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("fileStaticPath", fileStaticPath);
        jsonObject.put("fileAbsPath", fileAbsPath);
        redisService.setCacheObject(key, jsonObject);
    }

    private List<Map<String, Object>> getData(ExportExcelDto dto, String tableName, List<GenTableColumn> columns) {
        List<Map<String, Object>> list = null;
        FormDataDto condition = new FormDataDto();
        condition.setTable(tableName);
        condition.setFixedData(new JSONObject(dto.getCondition()));
        condition.setPageSize(Integer.MAX_VALUE);
        condition.setPageNo(1);
        AjaxResult ajaxResult = remoteObjService.getByMap(condition);
        if (AjaxResult.checkSuccess(ajaxResult)) {
            list = ((List<Map<String, Object>>) ajaxResult.get(AjaxResult.DATA_TAG));
            handleNullColumnValue(list, map(columns, GenTableColumn::getColumnName));
        }

        return list;
    }

    private List<Map<String, Object>> getData1(ExportExcelDto dto, String tableName, List<GenTableColumn> columns, String token) {
        List<Map<String, Object>> list = null;
        Map<String, Object> formData = dto.getCondition();
        String idCard = (String) formData.get("idCard");
        String userName = (String) formData.get("userName");
        String phoneNum = (String) formData.get("phoneNum");
        String jici = (String) formData.get("jici");
        String keyIndustries = (String) formData.get("keyIndustries");
        String vaccineName = (String) formData.get("vaccineName");
        String vaccinationPlace = (String) formData.get("vaccinationPlace");
        String isVaccination = (String) formData.get("isVaccination");
        String shouldBe = (String) formData.get("shouldBe");
        String shouldSlow = (String) formData.get("shouldSlow");
        Integer deptIdTemp = (Integer) formData.get("deptId");
        Long deptId;
        if (deptIdTemp == null) {
            LoginUser loginUser = redisService.getCacheObject(CacheConstants.LOGIN_TOKEN_KEY + token);
            deptId = loginUser.getSysUser().getDeptId();
        } else {
            deptId = deptIdTemp.longValue();
        }

        List<SysDept> sysDepts = remoteDeptService.listChildrenDepts(deptId);
        List<Long> deptIdList = new ArrayList<>();
        if (sysDepts != null && sysDepts.size() > 0) {
            for (SysDept sysDept : sysDepts) {
                Long id = sysDept.getId();
                deptIdList.add(id);
            }
        }

        FormDataDto condition = new FormDataDto();
        condition.setTable(tableName);
        JSONObject jsonObject = new JSONObject();
        jsonObject.put("dept_id", deptIdList);
        jsonObject.put("is_del", 'N');
        if (isNotEmpty(idCard)) {
            jsonObject.put("id_card", idCard);
        }

        if (isNotEmpty(phoneNum)) {
            jsonObject.put("phone_num", phoneNum);
        }

        if (isNotEmpty(userName)) {
            jsonObject.put("user_name", userName);
        }

        if (isNotEmpty(jici)) {
            jsonObject.put("jici", jici);
        }

        if (isNotEmpty(keyIndustries)) {
            jsonObject.put("key_industries", keyIndustries);
        }

        if (isNotEmpty(vaccineName)) {
            jsonObject.put("vaccine_name", vaccineName);
        }

        if (isNotEmpty(vaccinationPlace)) {
            jsonObject.put("vaccination_place", vaccinationPlace);
        }
        if (isNotEmpty(isVaccination)) {
            jsonObject.put("is_vaccination", isVaccination);
        }
        if (isNotEmpty(shouldBe)) {
            jsonObject.put("should_be", shouldBe);
        }

        if (isNotEmpty(shouldSlow)) {
            jsonObject.put("should_slow", shouldSlow);
        }

        condition.setFixedData(jsonObject);
        condition.setPageSize(Integer.MAX_VALUE);
        condition.setPageNo(1);
        AjaxResult ajaxResult = remoteObjService.getByMap(condition);
        if (AjaxResult.checkSuccess(ajaxResult)) {
            list = ((List<Map<String, Object>>) ajaxResult.get(AjaxResult.DATA_TAG));
            handleNullColumnValue(list, map(columns, GenTableColumn::getColumnName));
        }

        return list;
    }

    /**
     * 功能描述: 单独去查疫苗接种信息的
     *
     * @param info       info
     * @return java.util.List<java.util.Map < java.lang.String, java.lang.Object>>
     */
    private List<Map<String, Object>> listYmjz(VaccineInfoOperation info, List<GenTableColumn> columns) {
//        TableDataInfo list = remoteObjService.list(info);
//        LOGGER.info("查询结果：{}", JSON.toJSONString(list));
//        List<?> rows = list.getRows();
//        return JSON.parseObject(JSON.toJSONString(rows), new TypeReference<List<Map<String, Object>>>() {
//        }.getType());

        List<Map<String, Object>> list = null;
        FormDataDto condition = new FormDataDto();
        condition.setTable("vaccine_info");
        condition.setFixedData(new JSONObject());
        condition.setPageSize(Integer.MAX_VALUE);
        condition.setPageNo(1);
        AjaxResult ajaxResult = remoteObjService.getByMap(condition);
        if (AjaxResult.checkSuccess(ajaxResult)) {
            list = ((List<Map<String, Object>>) ajaxResult.get(AjaxResult.DATA_TAG));
            handleNullColumnValue(list, map(columns, GenTableColumn::getColumnName));
        }

        return list;
    }

    /**
     * 功能描述: 导出数据，只是sql不一样，其余的都是通用接口的内容
     *
     * @param response response
     * @param dto      tableName
     *                 empty(true=>只带表头的excel，不含数据, false=>带数据的excel)
     *                 condition 查询条件
     * @return com.boman.domain.dto.AjaxResult
     */
    @Override
    public AjaxResult statisticsByMonth(HttpServletResponse response, ExportExcelDto dto) {

        String tableName = dto.getTableName();
        Boolean empty = dto.getEmpty();
        ObjectUtils.requireNonNull(tableName, "exportExcelCommon tableName is empty");
        ObjectUtils.requireNonNull(empty, "exportExcelCommon empty is empty");

        ExcelUtil<JSONObject> util = new ExcelUtil<>(JSONObject.class);
        List<Map<String, Object>> list = null;
        if (BooleanUtils.isTrue(empty)) {
            list = new ArrayList<>(0);
        } else {
            AjaxResult ajaxResult = remoteAttendanceService.statisticsByMonth(dto.getCondition());
            if (AjaxResult.checkSuccess(ajaxResult)) {
                list = ((List<Map<String, Object>>) ajaxResult.get(AjaxResult.DATA_TAG));
                // 导出不需要user_id, 同时把map中的value换成int
                List<Map<String, Object>> newList = new ArrayList<>(list.size());
                for (Map<String, Object> map : list) {
                    map.remove("user_id");
                    for (Map.Entry<String, Object> entry : map.entrySet()) {
                        Object value = entry.getValue();
                        if (value instanceof Double) {
                            Double value1 = (Double) value;
                            entry.setValue(value1.intValue());
                        }
                    }

                    Map<String, Object> newMap = new LinkedHashMap<>(map.size());
                    newMap.put(USERNAME, map.get(USERNAME));
                    newMap.put(DEPT_NAME, map.get(DEPT_NAME));
                    newMap.put(ATTENDANCE_TABLE_LATE_SUM, map.get(ATTENDANCE_TABLE_LATE_SUM));
                    newMap.put(ATTENDANCE_TABLE_LEAVE_SUM, map.get(ATTENDANCE_TABLE_LEAVE_SUM));
                    newMap.put(ATTENDANCE_TABLE_LEAVE_OR_SUM, map.get(ATTENDANCE_TABLE_LEAVE_OR_SUM));
                    newMap.put(DATE, map.get(DATE));
                    newList.add(newMap);
                }

                list = newList;
            }
        }

        try {
            util.exportExcelCommon(response, list, "sheet1", null, empty, true);
        } catch (IOException e) {
            LOGGER.error("考勤统计导出失败，导出数据为：{}", list);
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 功能描述: 通用的导入接口
     *
     * @param dto@return java.util.List<com.alibaba.fastjson.JSONObject>
     */
    @Override
    public List<JSONObject> importExcelCommon(ImportExcelDto dto) throws Exception {
        return null;
    }

    public void handleNullColumnValue(List<Map<String, Object>> result, List<String> showData) {
        for (Map<String, Object> map : result) {
            Set<String> resultKeySet = map.keySet();
            for (String columnName : showData) {
                if (!resultKeySet.contains(columnName)) {
                    map.put(columnName, "");
                }
            }
        }
    }

    /**
     * 功能描述: 获取系统配置
     *
     * @return java.lang.String
     */
    @Override
    public String getConfigPath() {
        return localFilePrefix + "_._" + domain + "_._" + localFilePath;
    }
}
