package com.yida.data.user.service.impl;


import cc.mrbird.febs.common.redis.service.RedisService;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.ZipUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yida.data.attendance.dto.IssuedFaceDTO;
import com.yida.data.attendance.feign.RemoteAttendanceDeviceService;
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
import com.yida.data.common.core.entity.user.*;
import com.yida.data.common.core.exception.FebsException;
import com.yida.data.common.core.utils.Asserts;
import com.yida.data.common.core.utils.FileUtil;
import com.yida.data.user.dto.ListStaffDTO;
import com.yida.data.user.dto.ListStudentDTO;
import com.yida.data.user.mapper.EduStaffMapper;
import com.yida.data.user.mapper.EduStudentMapper;
import com.yida.data.user.mapper.EduUserDeviceMapper;
import com.yida.data.user.mapper.EduUserFaceMapper;
import com.yida.data.user.service.EduUserFaceService;
import com.yida.data.user.util.FaceUtil;
import com.yida.data.user.vo.ImportFaceVO;
import com.yida.data.user.vo.IssueFacePercentVO;
import com.yida.data.user.vo.UserInfoVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 用户人脸图像 Service实现
 *
 * @author zhanghaijun
 * @date 2021-06-16 14:14:52
 */
@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class EduUserFaceServiceImpl extends ServiceImpl
        <EduUserFaceMapper, EduUserFace> implements EduUserFaceService {

    private final EduStudentMapper eduStudentMapper;
    private final EduStaffMapper eduStaffMapper;
    private final EduUserDeviceMapper eduUserDeviceMapper;

    private final RemoteAttendanceDeviceService remoteAttendanceDeviceService;
    private final RedisService redisService;

    @Value("${febs.uploadUrl}")
    private String uploadUrl;

    @Override
    public String uploadUserFace(MultipartFile file, Long id, String name,
                                 Long schoolId, Integer type) {
        //处理图片到100k
        String path;
        try {
            path = FileUtil.uploadCompressBase64Img(file, 100L);
        } catch (Exception e) {
            log.error("msg:{}", e.getMessage());
            throw new FebsException("图片压缩失败，请重新上传");
        }
        //检查人脸
        Boolean containsFace = FaceUtil.containsFace(path);
        Asserts.isTrue(containsFace, "未检测到人脸，请重新上传图片");

        // 上传图片到媒资
        String imgPath = FileUtil.uploadFileToMediaServer(uploadUrl, path).split(",")[0];
        //删除本地图片
        cn.hutool.core.io.FileUtil.del(path);
        // 更新人脸
        updateUserUpLoadFace(imgPath, id, name, type);

        return imgPath;
    }

    /**
     * 单独处理前端人脸上传的图片
     *
     * @param picPath
     * @param id
     * @param name
     * @param type
     */
    public void updateUserUpLoadFace(String picPath, Long id, String name, Integer type) {
        // 新增更新人脸
        EduUserFace userFace = getOne(
                Wrappers.lambdaQuery(new EduUserFace()).eq(EduUserFace::getUserId, id).eq(EduUserFace::getUserType, type));
        if (userFace == null) {
            userFace = new EduUserFace();
            userFace.setUserId(id);
            userFace.setName(name);
            userFace.setUserType(type);
        }
        userFace.setPicPath(picPath);
        saveOrUpdate(userFace);
        // 更新头像
        if (type == 0) {
            EduStudent student = new EduStudent();
            student.setId(id);
            student.setAvatar(picPath);
            student.setCollectType(1);
            eduStudentMapper.updateById(student);
        } else if (type == 1) {
            EduStaff staff = new EduStaff();
            staff.setId(id);
            staff.setAvatar(picPath);
            staff.setCollectType(1);
            eduStaffMapper.updateById(staff);
        }
        List<EduUserDevice> userDeviceList = eduUserDeviceMapper.selectList(
                Wrappers.lambdaQuery(new EduUserDevice()).eq(EduUserDevice::getUserId, id).eq(EduUserDevice::getUserType, type));
        if (CollUtil.isNotEmpty(userDeviceList)) {
            // 用户已经被下发到设备，更新设备上的图像
            Map<Long, List<EduUserDevice>> userDeviceMap = userDeviceList.stream().collect(
                    Collectors.groupingBy(EduUserDevice::getFaceGroupId));
            for (Map.Entry<Long, List<EduUserDevice>> entry : userDeviceMap.entrySet()) {
                Long faceGroupId = entry.getKey();
                List<EduUserDevice> eduUserDevices = entry.getValue();
                // 设备id
                List<Long> deviceIds = eduUserDevices.stream().map(EduUserDevice::getDeviceId).distinct()
                        .collect(Collectors.toList());
                IssuedFaceDTO dto = new IssuedFaceDTO();
//                dto.setType(1);
                dto.setDeviceList(deviceIds);
                dto.setGroupId(faceGroupId);
                dto.setUserFaceList(Collections.singletonList(userFace));
                remoteAttendanceDeviceService.issuedFace(dto);
            }
        }
        // TODO 2023/3/1 异步同步大华
    }

    @Override
    public void uploadUserFaceBatch(String file, Long schoolId, Integer type, Long sysUserId, Integer importType) {
        int total;
        int finish = 0;
        int percent = 0;
        // 上传信息
        ImportFaceVO faceVO = new ImportFaceVO();
        faceVO.setFinish(0);
        faceVO.setFileName(cn.hutool.core.io.FileUtil.getName(file));
        faceVO.setPercent(0);
        redisService.set(CachePrefixConstant.IMPORT_FACE_KEY + sysUserId.toString(), faceVO);
        try {
            // 解压文件到本地
            File zipFile = new File(file);
            ZipUtil.unzip(zipFile, CharsetUtil.charset(CharsetUtil.GBK));
            // 遍历图片
            String imgPath;
            Long userId;
            String userName;
            String zipPath = zipFile.getAbsolutePath();
            List<File> files = cn.hutool.core.io.FileUtil.loopFiles(zipPath.split("\\.")[0]);
            total = files.size();
            // 无法导入的图片
            List<File> errorFiles = new ArrayList<>();
            for (File img : files) {
                String userFlag = img.getName().split("\\.")[0];
                // 按学号/手机号导入
                if (importType == 0) {
                    if (type == 0) {
                        EduStudent student = eduStudentMapper.selectOne(
                                Wrappers.lambdaQuery(new EduStudent()).eq(EduStudent::getStuNumber, userFlag)
                                        .eq(EduStudent::getSchoolId, schoolId));
                        userId = student != null ? student.getId() : null;
                        userName = student != null ? student.getStuName() : null;
                    } else {
                        EduStaff staff =
                                eduStaffMapper.selectOne(Wrappers.lambdaQuery(new EduStaff()).eq(EduStaff::getMobile, userFlag)
                                        .eq(EduStaff::getSchoolId, schoolId));
                        userId = staff != null ? staff.getId() : null;
                        userName = staff != null ? staff.getName() : null;
                    }
                } else { // 按名称导入
                    if (type == 0) {
                        List<EduStudent> studentList = eduStudentMapper.selectList(Wrappers.lambdaQuery(new EduStudent())
                                .eq(EduStudent::getStuName, userFlag)
                                .eq(EduStudent::getSchoolId, schoolId));
                        userId = CollUtil.isNotEmpty(studentList) && studentList.size() == 1 ? studentList.get(0).getId() : null;
                        userName = userId != null ? studentList.get(0).getStuName() : null;
                    } else {
                        List<EduStaff> staffList = eduStaffMapper.selectList(Wrappers.lambdaQuery(new EduStaff())
                                .eq(EduStaff::getMobile, userFlag)
                                .eq(EduStaff::getSchoolId, schoolId));
                        userId = CollUtil.isNotEmpty(staffList) && staffList.size() == 1 ? staffList.get(0).getId() : null;
                        userName = userId != null ? staffList.get(0).getName() : null;
                    }
                }
                // 未找到唯一对应人员
                if (userId == null) {
                    errorFiles.add(img);
                    continue;
                }
                // 压缩图片至1000KB
                File compressImg = null;
                try {
                    compressImg = FileUtil.compressImg(img, 100L);
                } catch (Exception e) {
                    log.error("压缩图片失败", e);
                    errorFiles.add(img);
                    continue;
                }
                imgPath = FileUtil.uploadFileToMediaServer(uploadUrl, compressImg);
                try {
                    updateUserFace(imgPath, userId, userName, type);
                } catch (Exception e) {
                    log.error("人脸识别失败, 图片名称: {}", img.getName(), e);
                    errorFiles.add(img);
                    continue;
                }
                // 进度增加更新进度
                if (finish * 100 / total > percent) {
                    faceVO.setPercent(finish * 100 / total);
                    redisService.set(CachePrefixConstant.IMPORT_FACE_KEY + sysUserId, faceVO);
                }
                finish++;
            }
            // 打包错误文件
            if (CollUtil.isNotEmpty(errorFiles)) {
                String failPath = FileUtil.getRandomPath("zip");
                ZipUtil.zip(new File(failPath), false, errorFiles.toArray(new File[0]));
                faceVO.setFailPath(failPath);
            }
        } catch (Exception e) {
            log.error("上传出现异常", e);
        } finally {
            faceVO.setFinish(1);
            redisService.set(CachePrefixConstant.IMPORT_FACE_KEY + sysUserId, faceVO);
        }
    }

    @Override
    public void uploadImage(File file, Long id, String name, Integer type) {
        updateUserFace(FileUtil.uploadFileToMediaServer(uploadUrl, file), id, name, type);
    }

    @Override
    public IssueFacePercentVO getIssuePercent(Long faceGroupId) {
        Integer total = (Integer) redisService.get(CachePrefixConstant.ISSUE_FACE_TOTAL + faceGroupId);
        Integer error = (Integer) redisService.get(CachePrefixConstant.ISSUE_FACE_ERROR + faceGroupId);
        Integer finish = (Integer) redisService.get(CachePrefixConstant.ISSUE_FACE_FINISH + faceGroupId);
        // 错误信息
        List<Object> objects = new ArrayList<>();
        // 错误信息键值集合
        Set<String> keys = redisService.keys(CachePrefixConstant.ISSUE_FACE_ERROR_MSG + faceGroupId);
        if (CollUtil.isNotEmpty(keys)) {
            keys.forEach(key -> objects.addAll(redisService.lGet(key, 0L, -1L)));
        }

        IssueFacePercentVO vo = null;
        if (total != null) {
            vo = new IssueFacePercentVO();
            vo.setErrorNum(error);
            // TODO: 2023/9/18 后续优化百分比信息，此处修改为最大100
            int percent = 0;
            if (Objects.nonNull(finish)) {
                if (total <= finish) {
                    percent = 100;
                } else {
                    percent = finish * 100 / total;
                }
            }
//            vo.setPercent(total != null && finish != null ? finish * 100 / total : 100);
            vo.setPercent(percent);
            vo.setFinish(vo.getPercent() < 100 ? 0 : 1);

            if (CollUtil.isNotEmpty(objects)) {
                List<UserInfoVO> msgList = new ArrayList<>();
                Map<Integer, List<EduUserFace>> userGroup = objects.stream().map(x -> (EduUserFace) x)
                        .collect(Collectors.groupingBy(EduUserFace::getUserType));
                // 查询学生信息
                if (userGroup.containsKey(0)) {
                    // 学生人脸信息
                    List<EduUserFace> studentFaces = userGroup.get(0);
                    // 学生信息
                    List<EduStudent> studentList = eduStudentMapper
                            .listStudent(ListStudentDTO.builder().studentIds(
                                            studentFaces.stream().map(EduUserFace::getUserId).collect(Collectors.toList()))
                                    .build());
                    msgList.addAll(studentList.stream().map(x -> {
                        UserInfoVO infoVO = new UserInfoVO();
                        infoVO.setName(x.getStuName());
                        infoVO.setDept(x.getGradeName() + x.getClassName());
                        infoVO.setNumber(x.getStuNumber());
                        infoVO.setDeviceName(studentFaces.stream()
                                .filter(userFace -> userFace.getUserId().equals(x.getId()))
                                .collect(Collectors.toList())
                                .get(0).getDeviceName());
                        return infoVO;
                    }).collect(Collectors.toList()));
                }
                // 查询职工信息
                if (userGroup.containsKey(1)) {
                    // 职工人脸信息
                    List<EduUserFace> staffFaces = userGroup.get(1);
                    // 职工信息
                    List<EduStaff> staffList = eduStaffMapper.listStaff(ListStaffDTO.builder().ids(
                                    staffFaces.stream().map(EduUserFace::getUserId).collect(Collectors.toList()))
                            .build());
                    msgList.addAll(staffList.stream().map(x -> {
                        UserInfoVO infoVO = new UserInfoVO();
                        infoVO.setName(x.getName());
                        infoVO.setDept(CollUtil.isNotEmpty(x.getStaffDept())
                                ? x.getStaffDept().stream().map(EduStaffDept::getDeptName).collect(Collectors.joining(","))
                                : null);
                        infoVO.setNumber(x.getMobile());
                        infoVO.setDeviceName(staffFaces.stream()
                                .filter(userFace -> userFace.getUserId().equals(x.getId()))
                                .collect(Collectors.toList())
                                .get(0).getDeviceName());
                        return infoVO;
                    }).collect(Collectors.toList()));
                }
                vo.setMsgList(msgList.stream().distinct().collect(Collectors.toList()));
            }
        }
        return vo;
    }

    @Override
    public void updateUserFace(String picPath, Long id, String name, Integer type) {
        // 下载头像到本地
        String localPath = com.yida.data.common.core.utils.FileUtil.downloadToLocal(picPath);
        log.info("本地地址: {}, 远程地址: {}", localPath, picPath);
        // 检测是否包含人脸
        Boolean containsFace = FaceUtil.containsFace(localPath);
        cn.hutool.core.io.FileUtil.del(localPath);
        Asserts.isTrue(containsFace, "未检测到人脸，请重新上传图片");
        // 新增更新人脸
        EduUserFace userFace = getOne(
                Wrappers.lambdaQuery(new EduUserFace()).eq(EduUserFace::getUserId, id).eq(EduUserFace::getUserType, type));
        if (userFace == null) {
            userFace = new EduUserFace();
            userFace.setUserId(id);
            userFace.setName(name);
            userFace.setUserType(type);
        }
        userFace.setPicPath(picPath);
        saveOrUpdate(userFace);
        // 更新头像
        if (type == 0) {
            EduStudent student = new EduStudent();
            student.setId(id);
            student.setAvatar(picPath);
            student.setCollectType(1);
            eduStudentMapper.updateById(student);
        } else if (type == 1) {
            EduStaff staff = new EduStaff();
            staff.setId(id);
            staff.setAvatar(picPath);
            staff.setCollectType(1);
            eduStaffMapper.updateById(staff);
        }
        List<EduUserDevice> userDeviceList = eduUserDeviceMapper.selectList(
                Wrappers.lambdaQuery(new EduUserDevice()).eq(EduUserDevice::getUserId, id).eq(EduUserDevice::getUserType, type));
        if (CollUtil.isNotEmpty(userDeviceList)) {
            // 用户已经被下发到设备，更新设备上的图像
            Map<Long, List<EduUserDevice>> userDeviceMap = userDeviceList.stream().collect(
                    Collectors.groupingBy(EduUserDevice::getFaceGroupId));
            for (Map.Entry<Long, List<EduUserDevice>> entry : userDeviceMap.entrySet()) {
                Long faceGroupId = entry.getKey();
                List<EduUserDevice> eduUserDevices = entry.getValue();
                // 设备id
                List<Long> deviceIds = eduUserDevices.stream().map(EduUserDevice::getDeviceId).distinct()
                        .collect(Collectors.toList());
                IssuedFaceDTO dto = new IssuedFaceDTO();
//                dto.setType(1);
                dto.setDeviceList(deviceIds);
                dto.setGroupId(faceGroupId);
                dto.setUserFaceList(Collections.singletonList(userFace));
                remoteAttendanceDeviceService.issuedFace(dto);
            }
        }
    }

    @Override
    public void downloadImportFail(Long sysUserId, HttpServletResponse response) {
        ImportFaceVO faceVO = (ImportFaceVO) redisService.get(CachePrefixConstant.IMPORT_FACE_KEY + sysUserId);
        String failPath = faceVO.getFailPath();
        Asserts.isTrue(StrUtil.isNotBlank(failPath), "未找到失败文件");
        try {
            FileUtil.download(failPath, "上传失败人脸.zip", true, response);
        } catch (Exception e) {
            log.error("下载上传人脸失败文件失败", e);
        }
    }

    @Override
    public void downloadIssueFail(List<UserInfoVO> data, HttpServletResponse response) {

    }
}
