package com.zcloud.basic.info.command;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONObject;
import com.alibaba.cola.dto.SingleResponse;
import com.alibaba.cola.exception.BizException;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.jjb.saas.framework.auth.model.SSOUser;
import com.jjb.saas.framework.auth.utils.AuthContext;
import com.zcloud.basic.info.command.convertor.ImgFilesCoConvertor;
import com.zcloud.basic.info.domain.config.DaHuaConfig;
import com.zcloud.basic.info.domain.gateway.ImgFilesGateway;
import com.zcloud.basic.info.domain.model.ImgFilesE;
import com.zcloud.basic.info.domain.utils.ImageCompressUtil;
import com.zcloud.basic.info.dto.ImgFilesAddCmd;
import com.zcloud.basic.info.dto.clientobject.ImgFilesCO;
import com.zcloud.basic.info.dto.clientobject.ImgFilesInfoCO;
import com.zcloud.basic.info.persistence.dataobject.ImgFilesDO;
import com.zcloud.basic.info.persistence.dataobject.UserDO;
import com.zcloud.basic.info.persistence.repository.ImgFilesRepository;
import com.zcloud.basic.info.persistence.repository.UserRepository;
import com.zcloud.gbscommon.utils.FileUtil;
import com.zcloud.gbscommon.utils.PageQueryHelper;
import com.zcloud.gbscommon.utils.Tools;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;


/**
 * web-app
 *
 * @Author zhangyue
 * @Date 2025-10-30 16:10:00
 */
@Component
@AllArgsConstructor
@Slf4j
public class ImgFilesAddExe {
    private final ImgFilesGateway imgFilesGateway;
    private final ImgFilesCoConvertor imgFilesCoConvertor;
    private final ImgFilesRepository imgFilesRepository;
    private final DaHuaConfig daHuaConfig;
    private final UserRepository userRepository;
    private final ImgFilesRemoveExe imgFilesRemoveExe;

    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<ImgFilesCO> execute(ImgFilesAddCmd cmd) {
        if (daHuaConfig.getDockFlag() == 1 && (cmd.getType() == 608 || cmd.getType() == 13)){
            try {
                for (MultipartFile multipartFile : cmd.getFiles()){
                    String base64ImgStr = FileUtil.multipartFileToBase64(multipartFile);
                    JSONObject resultJsonObject = daHuaConfig.uploadPersonAvatar(base64ImgStr);
                    // 如果不合格，直接返回不合格
                    if (resultJsonObject.getBool("success")){
                        cmd.setDaHuaFilePath(resultJsonObject.getJSONObject("data").getStr("fileUrl"));
                    } else {
                        return SingleResponse.buildFailure(resultJsonObject.toString());
                    }
                }
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        SSOUser ssoUser = AuthContext.getCurrentUser();
        Long tenantId = 1L;
        if (ssoUser != null && ssoUser.getTenantId() != null){
            tenantId = ssoUser.getTenantId();
        }
        ImgFilesE imgFilesE = new ImgFilesE();
        BeanUtils.copyProperties(cmd, imgFilesE);
        boolean res = false;
        ImgFilesCO imgFilesCO = new ImgFilesCO();
        try {
            String filePath = imgFilesE.initAdd(daHuaConfig.getDockFlag(), tenantId, imgFilesE);
            imgFilesCO.setFilePath(filePath);
            res = true;
        } catch (Exception e) {
            log.error("保存失败", e);
            throw new RuntimeException(e);
        }
        if (!res) {
            throw new BizException("保存失败");
        }
        return SingleResponse.of(imgFilesCO);
    }


    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<ImgFilesInfoCO> batchExecute(ImgFilesAddCmd cmd) {

        if (daHuaConfig.getDockFlag() == 1 && (cmd.getType() == 608 || cmd.getType() == 13)){
            Map<String, MultipartFile> filesMap = new HashMap<>();
            try {
                for (MultipartFile multipartFile : cmd.getFiles()){
                    String base64ImgStr = FileUtil.multipartFileToBase64(multipartFile);
                    JSONObject resultJsonObject = daHuaConfig.uploadPersonAvatar(base64ImgStr);
                    // 如果不合格，直接返回不合格
                    if (resultJsonObject.getBool("success")){
                        filesMap.put(resultJsonObject.getJSONObject("data").getStr("fileUrl"), multipartFile);
                    } else {
                        return SingleResponse.buildFailure(resultJsonObject.toString());
                    }
                }
                cmd.setFilesMap(filesMap);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }

        SSOUser ssoUser = AuthContext.getCurrentUser();
        Long tenantId = 1L;
        if (ssoUser != null && ssoUser.getTenantId() != null){
            tenantId = ssoUser.getTenantId();
        }
        ImgFilesE imgFilesE = new ImgFilesE();
        BeanUtils.copyProperties(cmd, imgFilesE);
        boolean res = false;
        ImgFilesInfoCO imgFilesCO = new ImgFilesInfoCO();
        try {
            List<ImgFilesE> imgFilesEList = new ArrayList<>();
            if (daHuaConfig.getDockFlag() == 1 && (cmd.getType() == 608 || cmd.getType() == 13)){
                imgFilesEList = imgFilesE.initDaHuaBatchAdd(tenantId, imgFilesE);
            } else {
                imgFilesEList = imgFilesE.initBatchAdd(tenantId, imgFilesE);
            }
            List<ImgFilesDO> imgFilesDOList = imgFilesCoConvertor.converEsToDOs(imgFilesEList);
            List<ImgFilesDO> resultImgFilesDOList = imgFilesDOList;
            if (cmd.getType() == 14 && cmd.getForeignKey() != null && !cmd.getForeignKey().trim().isEmpty()) {
                resultImgFilesDOList = syncIdCardFiles(cmd.getForeignKey(), imgFilesDOList);
            } else {
                imgFilesRepository.saveBatch(imgFilesDOList);
            }
            List<ImgFilesCO> imgFilesCOList = imgFilesCoConvertor.converDOsToCOs(resultImgFilesDOList);
            imgFilesCO.setFileList(imgFilesCOList);
            imgFilesCO.setForeignKey(imgFilesE.getForeignKey());
            res = true;
        } catch (Exception e) {
            log.error("保存失败", e);
            throw new RuntimeException(e);
        }
        if (!res) {
            throw new BizException("保存失败");
        }
        return SingleResponse.of(imgFilesCO);
    }

    /**
     * 同步身份证附件：
     * 1. 先保存当前用户本次上传的图片；
     * 2. 再回查当前用户最新有效的身份证图片集；
     * 3. 用当前用户的最新完整集覆盖同手机号的其他用户。
     */
    private List<ImgFilesDO> syncIdCardFiles(String currentUserId, List<ImgFilesDO> currentImgFilesDOList) throws Exception {
        // 先落库，再在同一事务内回查当前用户的完整身份证图片集。
        imgFilesRepository.saveBatch(currentImgFilesDOList);

        List<ImgFilesDO> currentAllIdCardFiles = listIdCardFilesByForeignKey(currentUserId);
        List<ImgFilesDO> currentLatestIdCardFiles = getLatestIdCardFiles(currentAllIdCardFiles);
        // 当前用户仍在使用的物理文件路径，后续清理旧记录时不能误删。
        Set<String> protectedFilePathSet = currentLatestIdCardFiles.stream()
                .map(ImgFilesDO::getFilePath)
                .filter(filePath -> filePath != null && !filePath.trim().isEmpty())
                .collect(Collectors.toSet());

        // 当前用户如果因为多次编辑产生了更老的身份证记录，这里只清理多余记录。
        List<ImgFilesDO> currentRedundantIdCardFiles = getRedundantIdCardFiles(currentAllIdCardFiles, currentLatestIdCardFiles);
        removeIdCardFiles(currentRedundantIdCardFiles, protectedFilePathSet);

        List<UserDO> userDOList = userRepository.getUserListByUserId(currentUserId);
        if (CollUtil.isEmpty(userDOList)) {
            return currentLatestIdCardFiles;
        }

        List<String> otherUserIdList = userDOList.stream()
                .map(UserDO::getUserId)
                .filter(userId -> userId != null && !userId.trim().isEmpty())
                .filter(userId -> !currentUserId.equals(userId))
                .distinct()
                .collect(Collectors.toList());

        if (CollUtil.isEmpty(otherUserIdList)) {
            return currentLatestIdCardFiles;
        }

        // 先清理其他用户旧的身份证记录，再按当前用户的最新完整集重建。
        removeOldIdCardFiles(otherUserIdList, protectedFilePathSet);
        List<ImgFilesDO> syncImgFilesDOList = new ArrayList<>();
        for (String userId : otherUserIdList) {
            syncImgFilesDOList.addAll(copyImgFiles(currentLatestIdCardFiles, userId));
        }

        if (CollUtil.isNotEmpty(syncImgFilesDOList)) {
            imgFilesRepository.saveBatch(syncImgFilesDOList);
        }
        return currentLatestIdCardFiles;
    }

    /**
     * 查询某个用户名下当前有效的身份证附件。
     */
    private List<ImgFilesDO> listIdCardFilesByForeignKey(String foreignKey) {
        Map<String, Object> params = new HashMap<>();
        params.put("eqForeignKey", foreignKey);
        params.put("eqType", 14);
        params.put("eqDeleteEnum", "FALSE");
        return imgFilesRepository.listAll(params);
    }

    /**
     * 现阶段前端不区分正反面，这里按最新两张作为当前有效身份证图片集。
     */
    private List<ImgFilesDO> getLatestIdCardFiles(List<ImgFilesDO> idCardFiles) {
        if (CollUtil.isEmpty(idCardFiles)) {
            return new ArrayList<>();
        }
        int keepCount = Math.min(idCardFiles.size(), 2);
        return new ArrayList<>(idCardFiles.subList(0, keepCount));
    }

    /**
     * 找出当前用户历史上多余的旧身份证记录，保留最新有效集，其余作为冗余记录处理。
     */
    private List<ImgFilesDO> getRedundantIdCardFiles(List<ImgFilesDO> allIdCardFiles, List<ImgFilesDO> latestIdCardFiles) {
        if (CollUtil.isEmpty(allIdCardFiles) || allIdCardFiles.size() <= latestIdCardFiles.size()) {
            return new ArrayList<>();
        }
        Set<Long> latestIdSet = latestIdCardFiles.stream()
                .map(ImgFilesDO::getId)
                .collect(Collectors.toSet());
        return allIdCardFiles.stream()
                .filter(imgFilesDO -> !latestIdSet.contains(imgFilesDO.getId()))
                .collect(Collectors.toList());
    }

    /**
     * 清理其他同手机号用户旧的身份证记录。
     * 这里只清理旧数据，当前用户仍在使用的物理文件路径会被保护，不会删除。
     */
    private void removeOldIdCardFiles(List<String> userIdList, Set<String> protectedFilePathSet) throws Exception {
        Map<String, Object> params = new HashMap<>();
        params.put("inForeignKey", userIdList.toArray(new String[0]));
        params.put("eqType", 14);
        params.put("eqDeleteEnum", "FALSE");

        List<ImgFilesDO> oldImgFilesDOList = imgFilesRepository.listAll(params);
        removeIdCardFiles(oldImgFilesDOList, protectedFilePathSet);
    }

    /**
     * 删除身份证记录时分两步处理：
     * 1. 数据记录一定删除；
     * 2. 物理文件只有在没有其他有效记录引用时才删除。
     */
    private void removeIdCardFiles(List<ImgFilesDO> imgFilesDOList, Set<String> protectedFilePathSet) throws Exception {
        if (CollUtil.isEmpty(imgFilesDOList)) {
            return;
        }

        List<Long> deleteIdList = imgFilesDOList.stream()
                .map(ImgFilesDO::getId)
                .collect(Collectors.toList());
        ImgFilesE imgFilesE = new ImgFilesE();
        List<String> filePathList = imgFilesDOList.stream()
                .map(ImgFilesDO::getFilePath)
                .filter(filePath -> filePath != null && !filePath.trim().isEmpty())
                .distinct()
                .collect(Collectors.toList());

        for (String filePath : filePathList) {
            // 当前有效集仍在使用的文件，直接跳过，避免单张编辑时把另一张误删。
            if (protectedFilePathSet.contains(filePath)) {
                continue;
            }
            // 只有确认没有其他有效记录再引用该路径时，才删除物理文件。
            if (canDeletePhysicalFile(filePath, deleteIdList)) {
                imgFilesE.deleteFile(filePath);
            }
        }

        Long[] ids = deleteIdList.toArray(new Long[0]);
        imgFilesRepository.deleteList(ids);
    }

    /**
     * 判断物理文件是否还被其他有效记录引用。
     * 如果还有其他记录在用，当前只删数据库记录，不删物理文件。
     */
    private boolean canDeletePhysicalFile(String filePath, List<Long> deleteIdList) {
        QueryWrapper<ImgFilesDO> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("file_path", filePath);
        queryWrapper.eq("delete_enum", "FALSE");
        if (CollUtil.isNotEmpty(deleteIdList)) {
            queryWrapper.notIn("id", deleteIdList);
        }
        List<ImgFilesDO> otherReferencedImgFiles = imgFilesRepository.list(queryWrapper);
        return CollUtil.isEmpty(otherReferencedImgFiles);
    }

    /**
     * 复制当前用户的最新身份证图片集到其他用户名下，生成新的主键和业务主键。
     */
    private List<ImgFilesDO> copyImgFiles(List<ImgFilesDO> sourceList, String foreignKey) {
        List<ImgFilesDO> targetList = new ArrayList<>();
        for (ImgFilesDO source : sourceList) {
            ImgFilesDO target = new ImgFilesDO();
            BeanUtils.copyProperties(source, target);
            target.setId(null);
            target.setImgFilesId(Tools.get32UUID());
            target.setForeignKey(foreignKey);
            targetList.add(target);
        }
        return targetList;
    }
}

