package com.ecep.contract.task;

import java.io.File;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import com.ecep.contract.CompanyFileType;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.MyProperties;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.service.CloudTycService;
import com.ecep.contract.service.CompanyFileService;
import com.ecep.contract.service.CompanyOldNameService;
import com.ecep.contract.vo.CompanyFileVo;
import com.ecep.contract.vo.CompanyOldNameVo;
import com.ecep.contract.vo.CompanyVo;

import lombok.Getter;
import lombok.Setter;

/**
 * 从下载目录检索公司文件的本地任务器
 * 用于在客户端本地执行从下载目录检索相关资质文件的操作
 */
public class CompanyFileRetrieveFromDownloadDirTasker extends Tasker<Object> {
    private static final Logger logger = LoggerFactory.getLogger(CompanyFileRetrieveFromDownloadDirTasker.class);

    @Getter
    @Setter
    private CompanyVo company;

    @Getter
    @Setter
    private String downloadDirPath;
    @Getter
    @Setter
    private boolean companyPathChanged = false;

    private CompanyFileService getCompanyFileService() {
        return getCachedBean(CompanyFileService.class);
    }

    private CompanyOldNameService getCompanyOldNameService() {
        return getCachedBean(CompanyOldNameService.class);
    }

    @Override
    protected Object execute(MessageHolder holder) throws Exception {
        updateTitle("从下载目录检索文件");

        // 获取下载目录
        File dir;
        if (StringUtils.hasText(downloadDirPath)) {
            dir = new File(downloadDirPath);
        } else {
            // 如果没有提供下载目录，则使用配置中的
            MyProperties myProperties = getBean(MyProperties.class);
            dir = myProperties.getDownloadDirectory();
        }

        // 检查下载目录是否存在
        if (!dir.exists()) {
            String errorMsg = "下载目录 " + dir.getAbsolutePath() + " 不存在，请检查";
            holder.warn(errorMsg);
            return false;
        }

        // 开始检索下载文件夹
        holder.info("开始检索 下载 文件夹：" + dir.getAbsolutePath() + "...");
        File[] files = dir.listFiles(File::isFile);

        // 检查检索是否成功
        if (files == null) {
            String errorMsg = "检索 下载 文件夹失败";
            holder.error(errorMsg);
            return false;
        }

        // 检查文件夹是否有文件
        if (files.length == 0) {
            String errorMsg = "下载 文件夹没有文件";
            holder.info(errorMsg);
            return false;
        }

        // 报告文件夹中的文件数量
        holder.info("下载 文件夹中共有文件 " + files.length + " 个文件");
        updateProgress(20, 100);
        //
        companyPathChanged = getCompanyService().makePathAbsent(company, holder);
        if (companyPathChanged) {
            holder.info("企业目录已创建或更新：" + company.getPath());
            company = getCompanyService().save(company);
        }
        updateProgress(25, 100);

        // 检查企业目录是否存在
        if (!StringUtils.hasText(company.getPath())) {
            holder.error("企业存储目录未设置，请检查");
            return false;
        }

        // 检查企业目录是否存在
        File home = new File(company.getPath());
        if (!home.exists()) {
            holder.error(company.getPath() + " 不存在，无法访问，请检查或者修改");
            return false;
        }

        // 获取所有曾用名
        List<CompanyOldNameVo> oldNames = getCompanyOldNameService().findAllByCompany(company);
        holder.info("企业曾用名共有 " + oldNames.size() + " 个");

        // 按照 企业名字 移动到对应的曾用名目录
        Map<String, File> oldNameOfDir = new HashMap<>();
        oldNameOfDir.put(company.getName(), home);
        toMap(oldNames, oldNameOfDir);
        updateProgress(30, 100);

        // 执行文件检索操作
        for (int i = 0; i < files.length; i++) {

            updateProgress(30 + (i * 50) / files.length, 100);

            File file = files[i];
            // 只处理文件
            if (!file.isFile()) {
                continue;
            }

            MessageHolder sub = holder.sub("[" + (i + 1) + "/" + files.length + "]");
            String fileName = file.getName();

            for (Map.Entry<String, File> entry : oldNameOfDir.entrySet()) {
                String companyName = entry.getKey();
                // 必须要包含公司名称否则无法区分
                if (!fileName.contains(companyName)) {
                    sub.debug(fileName);
                    continue;
                }

                // 文件存储的目的地目录
                File destPath = entry.getValue();
                if (destPath == null) {
                    destPath = home;
                }
                sub.info(fileName + " -> " + destPath);

                CompanyFileVo filled = fillDownloadFileType(company, file, companyName, destPath, sub);
                if (filled != null) {
                    filled.setCompanyId(company.getId());
                    getCompanyFileService().save(filled);
                    holder.info("导入 " + fileName + " 到 " + destPath.getAbsolutePath());
                }
            }
        }

        updateProgress(100, 100);
        return null;
    }

    void toMap(List<CompanyOldNameVo> oldNames, Map<String, File> oldNameOfDir) {
        for (CompanyOldNameVo oldName : oldNames) {
            String name = oldName.getName();
            if (!StringUtils.hasText(name)) {
                continue;
            }
            File dir = null;
            String path = oldName.getPath();
            if (StringUtils.hasText(path)) {
                dir = new File(path);
            }
            oldNameOfDir.put(name, dir);
        }
    }

    /**
     * 从文件名生成公司文件对象
     * 文件从下载目录中导入
     *
     * @param company     公司对象
     * @param file        导入的文件对象
     * @param companyName 公司名称
     * @param destDir     目标目录
     * @param holder      状态输出
     * @return 生成的公司文件对象，如果无法转换则返回null
     */
    private CompanyFileVo fillDownloadFileType(CompanyVo company, File file, String companyName, File destDir,
            MessageHolder holder) {
        String fileName = file.getName();
        // 天眼查的报告
        // 目前只有 基础版企业信用报告, 企业信用信息公示报告下载保存时的文件名中没有天眼查
        if (CloudTycService.isTycReport(fileName)) {
            CompanyFileVo companyFile = new CompanyFileVo();
            companyFile.setType(CompanyFileType.CreditReport);
            fillApplyDateAbsent(file, companyFile);

            String destFileName = fileName;
            // 重命名 基础版企业信用报告
            for (String report : Arrays.asList(
                    CloudServiceConstant.TYC_ENTERPRISE_ANALYSIS_REPORT,
                    CloudServiceConstant.TYC_ENTERPRISE_BASIC_REPORT,
                    CloudServiceConstant.TYC_ENTERPRISE_MAJOR_REPORT)) {

                if (fileName.contains(report)) {
                    LocalDate applyDate = companyFile.getApplyDate();
                    if (applyDate == null) {
                        applyDate = LocalDate.now();
                        companyFile.setApplyDate(applyDate);
                    }
                    String formatted = MyDateTimeUtils.format(applyDate);
                    String extension = StringUtils.getFilenameExtension(fileName);
                    destFileName = String.format("%s_%s_%s_%s.%s",
                            companyName, CloudServiceConstant.TYC_NAME, report, formatted, extension);
                }
            }

            // 重新设置 企业分析报告 未普通文件
            // if (fileName.contains(CloudTycService.TYC_ENTERPRISE_ANALYSIS_REPORT)) {
            // companyFile.setType(General);
            // }

            File dest = new File(destDir, destFileName);
            // 移动文件
            if (!file.renameTo(dest)) {
                // 移动失败时
                holder.warn(fileName + " 无法移动到 " + dest.getAbsolutePath());
                return null;
            }

            holder.info(fileName + " 移动到 " + dest.getAbsolutePath());
            companyFile.setFilePath(dest.getAbsolutePath());

            //
            if (companyFile.getExpiringDate() == null) {
                if (companyFile.getApplyDate() != null) {
                    companyFile.setExpiringDate(companyFile.getApplyDate().plusYears(1));
                }
            }
            return companyFile;
        }

        // 企业信用信息公示报告
        if (fileName.contains(CloudServiceConstant.TYC_ENTERPRISE_CREDIT_REPORT)) {
            CompanyFileVo companyFile = new CompanyFileVo();
            companyFile.setType(CompanyFileType.CreditInfoPublicityReport);
            fillApplyDateAbsent(file, companyFile);
            File dest = new File(destDir, fileName);

            // 移动文件
            if (!file.renameTo(dest)) {
                if (dest.exists()) {
                    // 尝试删除已经存在的文件
                    if (!dest.delete()) {
                        holder.warn("覆盖时，无法删除已存在的文件 " + dest.getAbsolutePath());
                        return null;
                    }
                    if (file.renameTo(dest)) {
                        List<CompanyFileVo> files = getCompanyFileService().findByCompanyAndPath(company,
                                dest.getAbsolutePath());
                        if (!files.isEmpty()) {
                            companyFile = files.getFirst();
                        }
                    } else {
                        holder.error(fileName + " 无法覆盖到 " + dest.getAbsolutePath());
                        return null;
                    }
                } else {
                    holder.error(fileName + " 无法移动到 " + dest.getAbsolutePath());
                    return null;
                }
            }

            holder.info(fileName + " 移动到 " + dest.getAbsolutePath());
            companyFile.setFilePath(dest.getAbsolutePath());
            return companyFile;
        }
        return null;
    }

    /**
     * 当 ApplyDate 未设置时，尝试使用文件名中包含的日期
     */
    private static void fillApplyDateAbsent(File file, CompanyFileVo companyFile) {
        LocalDate applyDate = companyFile.getApplyDate();
        if (applyDate != null) {
            return;
        }
        String fileName = file.getName();
        // 从文件名中提取日期
        LocalDate picked = MyDateTimeUtils.pickLocalDate(fileName);
        if (picked != null) {
            companyFile.setApplyDate(picked);
        }
    }

}