package com.ecep.contract.ds.vendor.task;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import com.ecep.contract.MessageHolder;
import com.ecep.contract.VendorFileType;
import com.ecep.contract.ds.company.CompanyFileUtils;
import com.ecep.contract.ds.company.service.CompanyService;
import com.ecep.contract.ds.vendor.model.VendorFile;
import com.ecep.contract.ds.vendor.service.VendorFileService;
import com.ecep.contract.ds.vendor.service.VendorService;
import com.ecep.contract.service.tasker.WebSocketServerTasker;
import com.ecep.contract.ui.Tasker;
import com.ecep.contract.util.FileUtils;
import com.ecep.contract.vo.CompanyVo;
import com.ecep.contract.vo.VendorFileVo;
import com.ecep.contract.vo.VendorVo;
import com.fasterxml.jackson.databind.JsonNode;

/**
 * 供应商文件重建任务
 * 1. 从供应商目录下递归遍历所有文件
 * 2. 检查数据库中是否存在该文件
 * 3. 如果不存在，则添加到数据库中
 * 4. 如果存在，则检查文件是否被修改
 * 5. 如果被修改，则更新数据库中的文件信息
 * 6. 如果文件不存在于供应商目录下，则从数据库中删除该文件
 */
public class VendorReBuildFilesTasker extends Tasker<Object> implements WebSocketServerTasker {
    private final Logger logger = LoggerFactory.getLogger(VendorReBuildFilesTasker.class);
    private VendorService vendorService;
    private VendorFileService vendorFileService;
    private VendorVo vendor;

    @Override
    public void init(JsonNode argsNode) {
        int vendorId = argsNode.get(0).asInt();
        vendorService = getCachedBean(VendorService.class);
        vendorFileService = getCachedBean(VendorFileService.class);
        vendor = vendorService.findById(vendorId);
    }

    @Override
    protected Object execute(MessageHolder holder) throws Exception {
        CompanyVo company = getCachedBean(CompanyService.class).findById(vendor.getCompanyId());

        holder.info("开始重建供应商[" + company.getName() + "]的文件信息...");
        updateProgress(0, 100);

        holder.info("正在读取数据库中的文件记录...");
        List<VendorFileVo> dbFiles = vendorFileService.findAllByVendor(vendor);
        holder.info("共找到 " + dbFiles.size() + " 条数据库记录，正在进行数据清理...");

        Map<String, VendorFileVo> map = new HashMap<>();
        boolean modified = fetchDbFiles(dbFiles, map, holder);
        updateProgress(30, 100);

        // 供应商目录下，准备用于移动到公司目录
        List<File> needMoveToCompanyPath = new ArrayList<>();
        // 有修改和新导入的文件
        List<VendorFileVo> retrieveFiles = new ArrayList<>();

        holder.info("正在扫描供应商目录[" + vendor.getPath() + "]中的文件...");
        fetchFiles(vendor.getPath(), needMoveToCompanyPath, retrieveFiles, map, holder);
        updateProgress(60, 100);

        // 移动文件到公司目录下 to company path
        if (!needMoveToCompanyPath.isEmpty()) {
            holder.info("发现 " + needMoveToCompanyPath.size() + " 个文件需要移动到公司目录，正在处理...");
            moveFileToCompany(needMoveToCompanyPath, holder);
            holder.info("文件移动完成");
        }

        holder.info("导入 " + retrieveFiles.size() + " 个文件");
        if (!retrieveFiles.isEmpty()) {
            holder.info("正在更新数据库记录...");
            int totalFiles = retrieveFiles.size();
            int processed = 0;
            // update db
            for (VendorFileVo v0 : retrieveFiles) {
                v0.setVendorId(vendor.getId());
                save(v0);
                processed++;
                updateProgress(60 + (processed * 40) / totalFiles, 100);
            }
            modified = true;
            holder.info("数据库更新完成");
        }
        updateProperty("modified", modified);

        updateProgress(100, 100);
        holder.info("文件重建任务完成");

        return null;

    }

    protected void moveFileToCompany(List<File> needMoveToCompanyPath, MessageHolder holder) {
        CompanyService companyService = getCachedBean(CompanyService.class);
        CompanyVo company = companyService.findById(vendor.getCompanyId());
        String companyPath = company.getPath();
        if (!StringUtils.hasText(companyPath)) {
            holder.warn("公司路径未设置，无法移动文件");
            return;
        }

        int movedCount = 0;
        int totalCount = needMoveToCompanyPath.size();
        for (File file : needMoveToCompanyPath) {
            File dest = new File(companyPath, file.getName());
            if (file.renameTo(dest)) {
                holder.info("移动文件: " + file.getAbsolutePath() + " -> " + dest.getAbsolutePath());
                movedCount++;
            } else {
                if (dest.exists()) {
                    if (file.delete()) {
                        holder.info("删除重复文件: " + file.getAbsolutePath());
                    } else {
                        holder.warn("无法删除重复文件: " + file.getAbsolutePath());
                    }
                } else {
                    holder.warn("无法移动文件: " + file.getAbsolutePath());
                }
            }
        }
        holder.info("文件移动完成: 共 " + totalCount + " 个文件，成功移动 " + movedCount + " 个");
    }

    /**
     * 从数据库中获取所有文件信息
     * 
     * @param dbFiles
     * @param map
     * @param holder
     * @return
     */
    protected boolean fetchDbFiles(List<VendorFileVo> dbFiles, Map<String, VendorFileVo> map, MessageHolder holder) {
        boolean modified = false;
        List<File> editFiles = new ArrayList<>();
        int deletedCount = 0;
        int updatedCount = 0;
        int totalCount = dbFiles.size();

        // 排除掉数据库中重复的
        for (int i = 0; i < dbFiles.size(); i++) {
            VendorFileVo dbFile = dbFiles.get(i);
            String filePath = dbFile.getFilePath();

            // 更新进度
            if (i % 10 == 0 || i == totalCount - 1) {
                updateProgress(10 + (i * 20) / totalCount, 100);
            }

            // 没有文件信息，无效记录，删除
            if (!StringUtils.hasText(filePath)) {
                deleteFile(dbFile);
                deletedCount++;
                continue;
            }

            // 文件不存在或者隐藏文件，删除记录
            File file = new File(filePath);
            if (!file.exists() || FileUtils.isHiddenFile(file)) {
                deleteFile(dbFile);
                deletedCount++;
                continue;
            }

            // old 是冲突的，按dbFiles顺序，旧的（list前面的）
            VendorFileVo old = map.put(filePath, dbFile);
            // 目录有重复删除
            if (old != null) {
                deleteFile(old);
                deletedCount++;
            }

            String editFilePath = dbFile.getEditFilePath();
            if (!Objects.equals(filePath, editFilePath)) {
                // 没有文件信息，无效记录，删除
                if (StringUtils.hasText(editFilePath)) {
                    File editFile = new File(editFilePath);
                    if (editFile.exists()) {
                        editFiles.add(editFile);
                    } else {
                        dbFile.setEditFilePath("");
                        updatedCount++;
                    }
                }
            }

            // 类型未设置，补充类型
            if (dbFile.getType() == null) {
                if (fillFileAsDefaultType(dbFile, file, holder)) {
                    modified = true;
                    updatedCount++;
                }
            }
        }

        // 处理编辑文件
        int editDeletedCount = 0;
        for (File editFile : editFiles) {
            String editFilePath = editFile.getAbsolutePath();
            VendorFileVo dup = map.remove(editFilePath);
            // 目录有重复删除
            if (dup != null) {
                deleteFile(dup);
                editDeletedCount++;
            }
        }

        holder.info("数据库清理完成: 删除 " + (deletedCount + editDeletedCount) + " 条无效记录，更新 " + updatedCount + " 条记录");

        return modified;
    }

    /**
     * 遍历扫描 path 目录下的文件， 并根据文件类型，填充到 retrieveFiles 中
     *
     * @param path
     * @param needMoveToCompanyPath 符合 CompanyFileUtils.isCompanyFile(file)
     *                              规则的文件，会加到此集合中
     * @param retrieveFiles
     * @param map
     * @param status
     * @param <T>
     * @param <F>
     */
    protected void fetchFiles(
            String path,
            List<File> needMoveToCompanyPath,
            List<VendorFileVo> retrieveFiles,
            Map<String, VendorFileVo> map,
            MessageHolder holder) {
        if (!StringUtils.hasText(path)) {
            return;
        }
        File dir = new File(path);
        if (!dir.exists()) {
            return;
        }
        File[] files = dir.listFiles();
        if (files == null) {
            return;
        }
        List<File> step1 = new ArrayList<>();
        for (File file : files) {
            // 只处理文件
            if (!file.isFile() || FileUtils.isHiddenFile(file)) {
                continue;
            }

            if (CompanyFileUtils.isCompanyFile(file)) {
                needMoveToCompanyPath.add(file);
                continue;
            }
            // 先把所有文件加到列表中
            step1.add(file);
        }

        if (!step1.isEmpty()) {
            // 第一步，处理已经存在的
            for (File file : new ArrayList<>(step1)) {
                String filePath = file.getAbsolutePath();
                if (map.containsKey(filePath)) {
                    // 已记录
                    VendorFileVo customerFile = map.get(filePath);
                    if (fillFile(customerFile, file, step1, holder)) {
                        retrieveFiles.add(customerFile);
                    }
                    step1.remove(file);
                }
            }

            // 第二步骤，再处理
            while (!step1.isEmpty()) {
                File file = step1.removeLast();
                VendorFileVo vendorFile = new VendorFileVo();
                vendorFile.setType(VendorFileType.General);
                vendorFile.setFilePath(file.getAbsolutePath());
                fillFile(vendorFile, file, step1, holder);
                if (vendorFile != null) {
                    retrieveFiles.add(vendorFile);
                }
            }
        }
    }

    protected boolean fillFile(VendorFileVo customerFile, File file, List<File> fileList,
            MessageHolder holder) {
        String fileName = file.getName();
        boolean modified = CompanyFileUtils.fillApplyDateAbsent(file, customerFile, VendorFileVo::getSignDate,
                VendorFileVo::setSignDate);
        // 评估表
        if (getCachedBean(VendorService.class).isEvaluationFile(fileName)) {
            if (fillFileAsEvaluationFile(customerFile, file, fileList, holder)) {
                modified = true;
            }
        } else {
            if (!Objects.equals(file.getAbsolutePath(), customerFile.getFilePath())) {
                customerFile.setFilePath(file.getAbsolutePath());
                modified = true;
            }
        }
        return modified;
    }

    /**
     * 以评价表单模板。填充文件信息
     *
     * @param customerFile 文件对象
     * @param file         相关文件
     * @param fileList     待处理文件列表
     * @param status       状态输出
     * @param <T>          类型类
     * @param <F>          文件类
     * @return true：文件对象有修改，否则返回false
     */
    protected boolean fillFileAsEvaluationFile(VendorFileVo customerFile, File file,
            List<File> fileList, MessageHolder holder) {
        boolean modified = setFileTypeAsEvaluationForm(customerFile);
        String fileName = file.getName();

        // 文件全路径
        String fileAbsolutePath = file.getAbsolutePath();

        if (FileUtils.isEditableFile(fileName)) {
            // 是可编辑文件时
            if (!Objects.equals(fileAbsolutePath, customerFile.getEditFilePath())) {
                customerFile.setEditFilePath(fileAbsolutePath);
                modified = true;
            }

            if (useAsEditableFile(customerFile, file, fileList, holder)) {
                modified = true;
            }
        } else if (FileUtils.isArchiveFile(fileName)) {
            // 存档文件时
            if (!Objects.equals(fileAbsolutePath, customerFile.getFilePath())) {
                customerFile.setFilePath(fileAbsolutePath);
                modified = true;
            }

            if (useAsArchiveFile(customerFile, file, fileList, holder)) {
                modified = true;
            }
        } else {
            customerFile.setFilePath(file.getAbsolutePath());
        }
        return modified;
    }

    protected boolean setFileTypeAsEvaluationForm(VendorFileVo file) {
        VendorFileType type = file.getType();
        if (type != VendorFileType.EvaluationForm) {
            file.setType(VendorFileType.EvaluationForm);
            return true;
        }
        return false;
    }

    private boolean useAsEditableFile(VendorFileVo customerFile, File file, List<File> fileList,
            MessageHolder holder) {
        if (fileList == null) {
            return false;
        }

        // 检查存档文件
        if (StringUtils.hasText(customerFile.getFilePath())) {
            // 移除 存档文件
            File archiveFile = new File(customerFile.getFilePath());
            fileList.remove(archiveFile);
            return false;
        }

        // 未关联存档文件，去找 fileList 查
        if (fileList.isEmpty()) {
            // 文件全路径
            String fileAbsolutePath = file.getAbsolutePath();
            // 当没有其他文件时，与 EditFile 相同
            if (!Objects.equals(fileAbsolutePath, customerFile.getFilePath())) {
                customerFile.setFilePath(fileAbsolutePath);
                return true;
            }
            return false;
        }

        File archiveFile = null;
        // 文件名
        String fileName = file.getName();
        // 文件名，不含后缀
        String name = StringUtils.stripFilenameExtension(fileName);
        for (File f : fileList) {
            // 查找存档文件
            if (f.getName().startsWith(name) && FileUtils.isArchiveFile(f.getName())) {
                archiveFile = f;
                break;
            }
        }
        // 没有匹配到文件
        if (archiveFile == null) {
            return false;
        }

        if (fileList.remove(archiveFile)) {
            customerFile.setFilePath(archiveFile.getAbsolutePath());
            return true;
        }

        return false;
    }

    private boolean useAsArchiveFile(VendorFileVo customerFile, File file, List<File> fileList,
            MessageHolder holder) {
        if (fileList == null) {
            return false;
        }

        // 未关联存档文件，去找 fileList 查
        if (fileList.isEmpty()) {
            return false;
        }

        // 检查可编辑文件
        if (StringUtils.hasText(customerFile.getEditFilePath())) {
            // 移除 可编辑文件
            File editFile = new File(customerFile.getEditFilePath());
            fileList.remove(editFile);
            return false;
        }

        File editFile = null;
        // 文件名
        String fileName = file.getName();
        // 文件名，不含后缀
        String name = StringUtils.stripFilenameExtension(fileName);
        for (File f : fileList) {
            // 查找存档文件
            if (f.getName().startsWith(name) && FileUtils.isEditableFile(f.getName())) {
                editFile = f;
                break;
            }
        }
        // 没有匹配到文件
        if (editFile == null) {
            return false;
        }

        if (fileList.remove(editFile)) {
            String editFilePath = editFile.getAbsolutePath();
            if (!Objects.equals(editFilePath, customerFile.getEditFilePath())) {
                customerFile.setEditFilePath(editFilePath);
                return true;
            }
        }

        return false;
    }

    protected boolean fillFileAsDefaultType(VendorFileVo dbFile, File file,
            MessageHolder holder) {
        dbFile.setType(VendorFileType.General);
        fillFile(dbFile, file, null, holder);
        save(dbFile);
        return true;
    }

    private void save(VendorFileVo dbFile) {
        VendorFile v1 = vendorFileService.getById(dbFile.getId());
        vendorFileService.updateByVo(v1, dbFile);
        vendorFileService.save(v1);
    }

    private void deleteFile(VendorFileVo dbFile) {
        VendorFile v1 = vendorFileService.getById(dbFile.getId());
        vendorFileService.delete(v1);
    }

}
