package com.ecep.contract.cloud.u8.ctx;

import static com.ecep.contract.SpringApp.getBean;

import java.io.File;
import java.text.NumberFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeParseException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import org.hibernate.Hibernate;
import org.springframework.util.StringUtils;

import com.ecep.contract.ContractFileType;
import com.ecep.contract.ContractPayWay;
import com.ecep.contract.MessageHolder;
import com.ecep.contract.MyDateTimeUtils;
import com.ecep.contract.constant.CloudServiceConstant;
import com.ecep.contract.ds.company.CompanyFileUtils;
import com.ecep.contract.ds.contract.service.ContractFileService;
import com.ecep.contract.ds.contract.service.ContractItemService;
import com.ecep.contract.ds.contract.service.ContractPayPlanService;
import com.ecep.contract.ds.contract.service.ContractService;
import com.ecep.contract.ds.customer.service.CompanyCustomerEntityService;
import com.ecep.contract.ds.customer.service.CompanyCustomerService;
import com.ecep.contract.ds.project.ProjectCtx;
import com.ecep.contract.ds.project.service.SaleTypeService;
import com.ecep.contract.ds.vendor.service.CompanyVendorEntityService;
import com.ecep.contract.ds.vendor.service.CompanyVendorService;
import com.ecep.contract.model.Company;
import com.ecep.contract.model.CompanyCustomer;
import com.ecep.contract.model.CompanyCustomerEntity;
import com.ecep.contract.model.CompanyVendor;
import com.ecep.contract.model.CompanyVendorEntity;
import com.ecep.contract.model.Contract;
import com.ecep.contract.model.ContractCatalog;
import com.ecep.contract.model.ContractFile;
import com.ecep.contract.model.ContractFileTypeLocal;
import com.ecep.contract.model.ContractGroup;
import com.ecep.contract.model.ContractItem;
import com.ecep.contract.model.ContractKind;
import com.ecep.contract.model.ContractPayPlan;
import com.ecep.contract.model.ContractType;
import com.ecep.contract.model.Employee;
import com.ecep.contract.model.Project;
import com.ecep.contract.model.ProjectSaleType;
import com.ecep.contract.util.NumberUtils;
import com.ecep.contract.util.TaxRateUtils;

import lombok.Getter;
import lombok.Setter;

/**
 * 合同上下文
 */
public class ContractCtx extends AbstractYongYouU8Ctx {
    @Setter
    private ContractService contractService;
    @Setter
    private ContractItemService contractItemService;
    @Setter
    private ContractFileService contractFileService;
    @Setter
    private ContractPayPlanService contractPayPlanService;
    @Setter
    private SaleTypeService saleTypeService;
    @Setter
    private VendorCtx vendorCtx;
    @Setter
    private CustomerCtx customerCtx;
    @Setter
    private CompanyCtx companyCtx;
    @Setter
    private InventoryCtx inventoryCtx;
    @Setter
    private ProjectCtx projectCtx;
    @Getter
    @Setter
    private int vendorEntityUpdateDelayDays = 1;
    @Getter
    @Setter
    private int customerEntityUpdateDelayDays = 1;
    public ContractService getContractService() {
        if (contractService == null) {
            contractService = getBean(ContractService.class);
        }
        return contractService;
    }
    ContractItemService getContractItemService() {
        if (contractItemService == null) {
            contractItemService = getBean(ContractItemService.class);
        }
        return contractItemService;
    }

    ContractPayPlanService getContractPayPlanService() {
        if (contractPayPlanService == null) {
            contractPayPlanService = getBean(ContractPayPlanService.class);
        }
        return contractPayPlanService;
    }

    ContractFileService getContractFileService() {
        if (contractFileService == null) {
            contractFileService = getBean(ContractFileService.class);
        }
        return contractFileService;
    }

    SaleTypeService getSaleTypeService() {
        if (saleTypeService == null) {
            saleTypeService = getBean(SaleTypeService.class);
        }
        return saleTypeService;
    }

    VendorCtx getVendorCtx() {
        if (vendorCtx == null) {
            vendorCtx = new VendorCtx();
            vendorCtx.from(this);
        }
        return vendorCtx;
    }

    CompanyCtx getCompanyCtx() {
        if (companyCtx == null) {
            companyCtx = new CompanyCtx();
            companyCtx.from(this);
        }
        return companyCtx;
    }

    CustomerCtx getCustomerCtx() {
        if (customerCtx == null) {
            customerCtx = new CustomerCtx();
            customerCtx.from(this);
        }
        return customerCtx;
    }

    InventoryCtx getInventoryCtx() {
        if (inventoryCtx == null) {
            inventoryCtx = new InventoryCtx();
            inventoryCtx.from(this);
        }
        return inventoryCtx;
    }

    ProjectCtx getProjectCtx() {
        if (projectCtx == null) {
            projectCtx = new ProjectCtx();
        }
        return projectCtx;
    }

    public void syncContract(Company company, MessageHolder holder) {
        List<Contract> list = getContractService().findAllByCompany(company);
        for (int i = 0; i < list.size(); i++) {
            Contract contract = list.get(i);
            syncContract(contract, holder);
        }
    }

    public void syncContract(Contract contract, MessageHolder holder) {
        boolean modified = updateContractDetailByGuid(contract, contract.getGuid(), holder);
        if (updateContractExec(contract, holder)) {
            modified = true;
        }

        if (modified) {
            contract = save(contract);
        }

        // fixed contract 对象需要更新保存了，syncContractItems 里可能会有新建的item，有引用，保存时会异常
        if (syncContractItems(contract, holder)) {
            modified = true;
            contract = save(contract);
        }
    }

    public boolean updateContractDetailByGuid(Contract contract, String guid, MessageHolder holder) {
        if (repository == null) {
            return false;
        }
        Map<String, Object> data;
        try {
            data = repository.queryContractDetail(guid);
        } catch (Exception e) {
            throw new RuntimeException("查询合同详情失败, guid=" + guid, e);
        }
        return applyContractDetail(contract, data, holder);
    }

    /**
     * 应用合同数据
     */
    public boolean applyContractDetail(Contract contract, Map<String, Object> rs, MessageHolder holder) {
        if (rs == null || rs.isEmpty()) {
            holder.warn("CM_Contract_B 中未检索到合同数据");
            return false;
        }

        boolean modified = false;
        String guid = (String) rs.get("GUID");
        String code = (String) rs.get("strContractID");
        String name = (String) rs.get("strContractName");
        String parentCode = (String) rs.get("strParentID");
        Double totalCurrency = (Double) rs.get("dblTotalCurrency");

        String description = (String) rs.get("strContractDesc");

        String typeCode = (String) rs.get("strContractType");
        String groupCode = (String) rs.get("strContractGrp");
        String kindName = (String) rs.get("strContractKind");
        String payWay = (String) rs.get("strWay");
        String unit = (String) rs.get("strBisectionUnit");

        if (updateText(contract::getGuid, contract::setGuid, guid, holder, "GUID")) {
            modified = true;
        }
        if (updateText(contract::getCode, contract::setCode, code, holder, "合同编号")) {
            modified = true;
        }
        if (updateText(contract::getName, contract::setName, name, holder, "合同名称")) {
            modified = true;
        }
        if (StringUtils.hasText(parentCode)) {
            if (updateText(contract::getParentCode, contract::setParentCode, parentCode, holder, "父合同号")) {
                modified = true;
            }
        }
        if (updatePayWay(contract, payWay, holder)) {
            modified = true;
        }
        if (updateTypeByCode(contract, typeCode, holder)) {
            modified = true;
        }
        if (updateGroupByCode(contract, groupCode, holder)) {
            modified = true;
        }
        if (updateKindByCode(contract, kindName, holder)) {
            modified = true;
        }
        if (updateEmployeeByCode(contract, (String) rs.get("strPersonID"), holder)) {
            modified = true;
        }
        if (updateLocalDate(contract::getOrderDate, contract::setOrderDate, (String) rs.get("strContractOrderDate"),
                holder, "下单日期")) {
            modified = true;
        }
        if (updateLocalDate(contract::getStartDate, contract::setStartDate, (String) rs.get("strContractStartDate"),
                holder, "开始日期")) {
            modified = true;
        }
        if (updateLocalDate(contract::getEndDate, contract::setEndDate, (String) rs.get("strContractEndDate"), holder,
                "结束日期")) {
            modified = true;
        }
        if (updateLocalDate(contract::getSetupDate, contract::setSetupDate, (String) rs.get("strSetupDate"), holder,
                "提交日期")) {
            modified = true;
        }
        if (updateLocalDate(contract::getInureDate, contract::setInureDate, (String) rs.get("strInureDate"), holder,
                "生效日期")) {
            modified = true;
        }
        if (updateLocalDate(contract::getVaryDate, contract::setVaryDate, (String) rs.get("dtVaryDate"), holder,
                "变更日期")) {
            modified = true;
        }
        if (updateEmployeeByName(contract::getSetupPerson, contract::setSetupPerson, (String) rs.get("strSetupPerson"),
                holder, "提交人")) {
            modified = true;
        }
        if (updateEmployeeByName(contract::getInurePerson, contract::setInurePerson, (String) rs.get("strInurePerson"),
                holder, "生效人")) {
            modified = true;
        }
        if (updateEmployeeByName(contract::getVaryPerson, contract::setVaryPerson, (String) rs.get("strVaryPerson"),
                holder, "修改人")) {
            modified = true;
        }
        if (totalCurrency != null) {
            if (contract.getAmount() == null) {
                contract.setAmount(0d);
                modified = true;
            } else {
                if (!NumberUtils.equals(totalCurrency, contract.getAmount())) {
                    holder.warn("注意合同金额和合计总金额不一致：" + totalCurrency);
                }
            }
        }
        if (updateAppendText(contract::getDescription, contract::setDescription, description, holder, "备注")) {
            modified = true;
        }

        switch (contract.getPayWay()) {
            case PAY -> {
                if (updateCompanyByVendorUnitCode(contract, unit, holder)) {
                    modified = true;
                }
            }
            case RECEIVE -> {
                if (updateCompanyByCustomerUnitCode(contract, unit, holder)) {
                    modified = true;
                }
            }
            default -> {
                holder.warn("未知合同付款方向:" + payWay);
            }
        }

        if (contract.getType() != null && contract.getPayWay() != null) {
            ContractType type = contract.getType();
            if (!Hibernate.isInitialized(type)) {
                type = getContractService().findTypeById(type.getId());
                contract.setType(type);
            }
            if (!type.getDirection().equals(contract.getPayWay().getText())) {
                holder.warn("合同类型：" + type.getName() + "的付款方向与付款方式不一致：" + contract.getPayWay().getText());
            }
        }

        if (contract.getKind() != null && contract.getType() != null) {
            ContractKind kind = contract.getKind();
            if (!Hibernate.isInitialized(kind)) {
                kind = getContractService().findKindById(kind.getId());
                contract.setKind(kind);
            }
            ContractType type = contract.getType();
            if (!Hibernate.isInitialized(type)) {
                type = getContractService().findTypeById(type.getId());
                contract.setType(type);
            }
            if (!kind.getName().equals(type.getTitle())) {
                holder.warn("合同分类的名称：" + kind.getName() + "与合同类型的标题：" + type.getName() + "不一致");
            }
        }
        return modified;
    }

    /**
     * 更新合同
     *
     * @param contract 更新的合同对象
     * @param unit     合同对应的单位代码
     * @param holder   消息处理对象
     */
    private boolean updateCompanyByVendorUnitCode(Contract contract, String unit, MessageHolder holder) {
        Company company = contract.getCompany();
        boolean modified = false;
        boolean vendorModified = false;
        CompanyVendorService vendorService = getCompanyVendorService();
        CompanyVendorEntityService vendorEntityService = getCompanyVendorEntityService();
        CompanyVendorEntity entity = vendorEntityService.findByCode(unit);
        if (entity == null) {
            entity = new CompanyVendorEntity();
            entity.setCode(unit);
        }
        entity = updateVendorEntityDetailByCode(entity, unit, holder);

        CompanyVendor vendor = entity.getVendor();
        if (vendor == null) {
            if (company != null) {
                vendor = vendorService.findByCompany(company);
            }
        } else {
            if (!Hibernate.isInitialized(vendor)) {
                vendor = vendorService.findById(vendor.getId());
            }
            if (vendor.getCompany() == null) {
                vendor.setCompany(company);
                vendorModified = true;
            } else {
                if (company == null) {
                    company = vendor.getCompany();
                } else if (!Objects.equals(company, vendor.getCompany())) {
                    holder.error(
                            "供应商的企业和合同的企业不一致， 供应商的企业:" + vendor.getCompany().getId() + ",合同的企业:" + company.getId());
                }
            }
        }
        if (vendor == null) {
            vendor = new CompanyVendor();
            vendor.setCompany(company);
            vendorModified = true;
        }
        if (vendor.getDevelopDate() == null
                || (entity.getDevelopDate() != null && vendor.getDevelopDate().isAfter(entity.getDevelopDate()))) {
            vendor.setDevelopDate(entity.getDevelopDate());
            vendorModified = true;
            holder.info("供应商的开发日期修改为：" + entity.getDevelopDate());
        }
        if (vendorModified) {
            vendor = vendorService.save(vendor);
        }

        // 确定 company
        if (company == null) {
            company = getCompanyCtx().findOrCreateByNameOrAbbName(entity.getName(), entity.getAbbName(),
                    entity.getDevelopDate(), holder);
        }

        if (updateCompany(contract, company, holder)) {
            modified = true;
        }

        return modified;
    }

    private boolean updateCompany(Contract contract, Company company, MessageHolder holder) {
        if (company == null) {
            return false;
        }
        if (Objects.equals(contract.getCompany(), company)) {
            return false;
        }
        if (!Hibernate.isInitialized(company)) {
            company = getCompanyService().findById(company.getId());
        }
        contract.setCompany(company);
        holder.info("关联至 " + company.getName());
        return true;
    }

    private CompanyVendorEntity updateVendorEntityDetailByCode(CompanyVendorEntity entity, String unitCode,
            MessageHolder holder) {
        if (vendorEntityUpdateDelayDays > 0) {
            LocalDateTime today = LocalDateTime.now();
            if (entity.getFetchedTime() != null) {
                LocalDateTime plussed = entity.getFetchedTime().plusDays(vendorEntityUpdateDelayDays);
                if (!plussed.isBefore(today)) {
                    holder.info("供应商表项 #" + entity.getId() + " 的更新时间：" + entity.getFetchedTime() + "，未超过"
                            + vendorEntityUpdateDelayDays + "天，将不再更新");
                    return entity;
                }
            }
            entity = getVendorCtx().updateVendorEntityDetailByCode(entity, unitCode, holder);
            entity.setFetchedTime(today);
            return getVendorCtx().save(entity);
        }
        return getVendorCtx().updateVendorEntityDetailByCode(entity, unitCode, holder);
    }

    private CompanyCustomerEntity updateCustomerEntityDetailByCode(CompanyCustomerEntity entity, String unitCode,
            MessageHolder holder) {
        if (customerEntityUpdateDelayDays > 0) {
            LocalDateTime today = LocalDateTime.now();
            if (entity.getFetchedTime() != null) {
                LocalDateTime plussed = entity.getFetchedTime().plusDays(customerEntityUpdateDelayDays);
                if (!plussed.isBefore(today)) {
                    holder.info("客户表项 #" + entity.getId() + " 的更新时间：" + entity.getFetchedTime() + "，未超过"
                            + customerEntityUpdateDelayDays + "天，将不再更新");
                    return entity;
                }
            }
            entity = getCustomerCtx().updateCustomerEntityDetailByCode(entity, unitCode, holder);
            entity.setFetchedTime(today);
            return getCustomerCtx().save(entity);
        }
        return getCustomerCtx().updateCustomerEntityDetailByCode(entity, unitCode, holder);
    }

    private boolean updateCompanyByCustomerUnitCode(Contract contract, String unit, MessageHolder holder) {
        Company company = contract.getCompany();
        boolean modified = false;
        boolean customerModified = false;
        CompanyCustomerEntityService customerEntityService = getCompanyCustomerEntityService();
        CompanyCustomerService customerService = getCompanyCustomerService();
        CompanyCustomerEntity entity = customerEntityService.findByCustomerCode(unit);
        if (entity == null) {
            entity = new CompanyCustomerEntity();
            entity.setCode(unit);
        }
        entity = updateCustomerEntityDetailByCode(entity, unit, holder);

        CompanyCustomer customer = entity.getCustomer();
        if (customer == null) {
            if (company != null) {
                customer = customerService.findByCompany(company);
            }
        } else {
            if (!Hibernate.isInitialized(customer)) {
                customer = customerService.findById(customer.getId());
            }
            if (customer.getCompany() == null) {
                customer.setCompany(company);
                customerModified = true;
            } else {
                if (company == null) {
                    company = customer.getCompany();
                } else if (!Objects.equals(company, customer.getCompany())) {
                    holder.error(
                            "客户的企业和合同的企业不一致， 客户的企业:" + customer.getCompany().getId() + ",合同的企业:" + company.getId());
                }
            }
        }
        if (customer == null) {
            customer = new CompanyCustomer();
            customer.setCompany(company);
            customerModified = true;
        }
        if (customer.getDevelopDate() == null
                || (entity.getDevelopDate() != null && customer.getDevelopDate().isAfter(entity.getDevelopDate()))) {
            customer.setDevelopDate(entity.getDevelopDate());
            customerModified = true;
            holder.info("客户的开发日期修改为：" + entity.getDevelopDate());
        }
        if (customerModified) {
            customer = customerService.save(customer);
        }
        // 确定 company
        if (company == null) {
            company = getCompanyCtx().findOrCreateByNameOrAbbName(entity.getName(), entity.getAbbName(),
                    entity.getDevelopDate(), holder);
        }

        if (updateCompany(contract, company, holder)) {
            modified = true;
        }

        return modified;
    }

    private boolean updatePayWay(Contract contract, String way, MessageHolder holder) {
        if (!StringUtils.hasText(way)) {
            holder.warn("无付款方向");
            return false;
        }
        ContractPayWay payWay = ContractPayWay.valueOfText(way);
        if (payWay == null) {
            holder.warn("付款方向无法识别:" + way);
            return false;
        }
        if (!Objects.equals(contract.getPayWay(), payWay)) {
            contract.setPayWay(payWay);
            holder.info("付款方向修改为: " + way);
            return true;
        }
        return false;
    }

    private boolean updateTypeByCode(Contract contract, String typeCode, MessageHolder holder) {
        ContractType type = getContractService().findTypeByCode(typeCode);
        if (!Objects.equals(contract.getType(), type)) {
            contract.setType(type);
            holder.info("合同类型修改为: " + type.getName());
            return true;
        }
        return false;
    }

    private boolean updateGroupByCode(Contract contract, String groupCode, MessageHolder holder) {
        ContractGroup group = getContractService().findGroupByCode(groupCode);
        if (!Objects.equals(contract.getGroup(), group)) {
            contract.setGroup(group);
            holder.info("合同分组修改为: " + group.getName());
            return true;
        }
        return false;
    }

    private boolean updateKindByCode(Contract contract, String kindName, MessageHolder holder) {
        ContractKind kind = getContractService().findKindByName(kindName);
        if (!Objects.equals(contract.getKind(), kind)) {
            contract.setKind(kind);
            holder.info("合同分类修改为: " + kind.getName());
            return true;
        }
        return false;
    }

    private boolean updateEmployeeByCode(Contract contract, String personId, MessageHolder holder) {
        boolean updated = updateEmployeeByCode(contract::getEmployee, contract::setEmployee, personId, holder, "业务员");
        Employee employee = contract.getEmployee();
        if (employee == null) {
            holder.warn("* 合同业务员：" + personId + " 未导入");
        } else {
            if (!Hibernate.isInitialized(employee)) {
                employee = getEmployeeService().findById(employee.getId());
            }
            if (!employee.isActive()) {
                holder.warn("业务员 " + employee.getName() + " 已经不可用，其离职日期：" + employee.getLeaveDate());
            }
        }
        return updated;
    }

    public Contract findContractByCode(String contractCode) {
        return getContractService().findByCode(contractCode);
    }

    public boolean syncByVendorEntity(CompanyVendor companyVendor, CompanyVendorEntity entity, MessageHolder holder) {
        String code = entity.getCode();
        holder.debug("同步供应商相关项 " + code + "," + entity.getName() + " 的合同");
        if (!StringUtils.hasText(code)) {
            holder.warn("系统编号 " + code + " 为空，跳过");
            return false;
        }
        AtomicBoolean updated = new AtomicBoolean(false);

        try (Stream<Map<String, Object>> stream = repository.queryAllContractByUnitForStream(entity.getCode(),
                ContractPayWay.PAY)) {
            stream.forEach(rs -> {
                if (updateContract(rs, holder)) {
                    updated.set(true);
                }
            });
        }
        return updated.get();
    }

    /**
     * 同步客户数据
     * 1. 同步客户信息
     * 2. 同步客户的合同
     * 客户数据要通过 CompanyCustomerEntity 表关联来从用友数据库中读取
     */
    public boolean syncByCustomerEntity(CompanyCustomer companyCustomer, CompanyCustomerEntity entity,
            MessageHolder holder) {
        String code = entity.getCode();
        holder.debug("同步客户相关项 " + code + "," + entity.getName() + " 的合同");
        if (!StringUtils.hasText(code)) {
            holder.warn("系统编号 " + code + " 为空，跳过");
            return false;
        }
        AtomicBoolean updated = new AtomicBoolean(false);
        try (Stream<Map<String, Object>> stream = repository.queryAllContractByUnitForStream(entity.getCode(),
                ContractPayWay.RECEIVE)) {
            stream.forEach(rs -> {
                if (updateContract(rs, holder)) {
                    updated.set(true);
                }
            });
        }
        return updated.get();
    }

    public boolean updateContract(String contractID, String guid, MessageHolder holder) {
        Contract contract = findOrCreatByGuidOrContractId(guid, contractID, holder);
        boolean updated = false;
        boolean modified = applyContractDetail(contract, repository.queryContractDetail(guid), holder);
        if (modified) {
            contract = getContractService().save(contract);
            updated = true;
        }
        return false;
    }

    public Contract findOrCreatByGuidOrContractId(String guid, String contractId, MessageHolder holder) {
        ContractService service = getContractService();
        UUID.fromString(guid);
        Contract contract = service.findByGuid(guid);
        if (contract == null) {
            holder.debug("根据GUID未查找到合同, GUID: " + guid);
            contract = service.findByCode(contractId);
            if (contract == null) {
                holder.debug("根据合同号未查找到合同, 合同号: " + contractId);

                contract = service.createNew();
                contract.setGuid(guid);
                contract.setCode(contractId);
                contract = service.save(contract);
                holder.info("新建合同：" + contractId + ", GUID: " + guid);
            }
        }
        return contract;
    }

    private boolean updateContract(Map<String, Object> rs, MessageHolder holder) {
        String contractID = (String) rs.get("strContractID");
        String guid = (String) rs.get("GUID");
        boolean updated = false;
        Contract contract = findOrCreatByGuidOrContractId(guid, contractID, holder);
        boolean modified = applyContractDetail(contract, rs, holder);
        if (modified) {
            contract = getContractService().save(contract);
            updated = true;
        }
        return updated;
    }

    public boolean updateParentContract(Contract contract, MessageHolder holder) {
        // 如果已经由父合同编号，跳过
        if (StringUtils.hasText(contract.getParentCode())) {
            return false;
        }
        // 判断合同号是否是子合同
        if (!getContractService().isSubContract(contract)) {
            return false;
        }

        // 采购合同
        if (contract.getPayWay() != ContractPayWay.PAY) {
            return false;
        }

        String parentCode = getContractService().getParentCode(contract);
        holder.info("尝试查找主合同：" + parentCode);
        List<Contract> list = getContractService().findByCodeStartsWith(ContractPayWay.RECEIVE, parentCode);
        if (list.isEmpty()) {
            holder.warn("没有找到以" + parentCode + "开头的销售合同，匹配主合同失败!");
            return false;
        }
        Optional<Contract> any = list.stream().filter(c -> !c.getCode().contains("-")).findAny();
        if (any.isEmpty()) {
            holder.warn("找到的合同号都不符合要求：" + list.stream().map(Contract::getCode).collect(Collectors.joining(",")) + "!");
            return false;
        }
        Contract parent = any.get();
        holder.info("找到主合同：" + parent.getCode());
        return updateText(contract::getCode, contract::setParentCode, parent.getCode(), holder, "主合同");
    }

    public boolean updateContractExec(Contract contract, MessageHolder holder) {
        if (repository == null) {
            return false;
        }
        Map<String, Object> rs = repository.sumContractExec(contract.getCode());
        if (rs == null) {
            return false;
        }
        boolean modified = false;
        double execQuantity = (double) Objects.requireNonNullElse(rs.get("execQuantity"), 0d);
        double execAmount = (double) Objects.requireNonNullElse(rs.get("execAmount"), 0d);
        double execUnTaxAmount = (double) Objects.requireNonNullElse(rs.get("execUnTaxAmount"), 0d);

        if (updateNumber(contract::getExecQuantity, contract::setExecQuantity, execQuantity, holder, "执行数")) {
            modified = true;
        }
        if (updateNumber(contract::getExecAmount, contract::setExecAmount, execAmount, holder, "执行金额")) {
            modified = true;
        }
        if (updateNumber(contract::getExecUnTaxAmount, contract::setExecUnTaxAmount, execUnTaxAmount, holder,
                "执行不含税金额")) {
            modified = true;
        }
        return modified;
    }

    /**
     * 同步合同项目信息
     *
     * @param contract 合同
     * @param holder   消息
     */
    public boolean syncContractItems(Contract contract, MessageHolder holder) {
        if (repository == null) {
            return false;
        }
        holder.debug("- 同步合同条目 -");
        ContractItemService itemService = getContractItemService();
        boolean changed = false;

        List<ContractItem> items = itemService.findAllByContract(contract);
        holder.debug("查找到 " + items.size() + " 条合同条目记录在数据库中");

        Map<Integer, ContractItem> itemMap = items.stream()
                .collect(Collectors.toMap(ContractItem::getRefId, item -> item));
        List<Map<String, Object>> ds = repository.findAllContractItemByContractCode(contract.getCode());
        holder.debug("查找到 " + ds.size() + " 条合同条目记录在 " + CloudServiceConstant.U8_NAME);

        List<ContractItem> updates = new ArrayList<>();
        for (Map<String, Object> map : ds) {
            // holder.debug("条目：" + map.toString());
            int refId = (Integer) map.get("ID");
            ContractItem contractItem = itemMap.remove(refId);
            boolean modified = false;
            if (contractItem == null) {
                contractItem = new ContractItem();
                contractItem.setContract(contract);
                contractItem.setRefId(refId);
                modified = true;

                holder.info("新增合同条目");
            }
            MessageHolder subHolder = holder.sub("--| #" + refId + " - ");
            if (applyContractItemDetail(contractItem, map, subHolder)) {
                modified = true;
            }
            if (modified) {
                contractItem.setUpdateDate(LocalDateTime.now());
                contractItem = itemService.save(contractItem);
                changed = true;
            }
            updates.add(contractItem);
        }
        if (!itemMap.isEmpty()) {
            for (ContractItem item : itemMap.values()) {
                itemService.delete(item);
            }
            changed = true;
        }

        if (sumItems(contract, updates, holder)) {
            changed = true;
        }

        return changed;
    }

    private boolean applyContractItemDetail(ContractItem item, Map<String, Object> map, MessageHolder holder) {
        Contract contract = item.getContract();
        if (contract == null) {
            holder.error("条目没有合同");
            return false;
        }

        String code = (String) map.get("strCode");
        String spec = (String) map.get("cInvStd");
        String title = (String) map.get("strName");
        double amount = (double) map.get("dblQuantity");
        String unit = (String) map.get("strMeasureUnit");
        double taxRate = (double) map.get("dblTaxRatio");
        double taxPrice = (double) map.get("dblPrice");
        double exclusiveTaxPrice = (double) map.get("dblUntaxPrice");

        String inventoryCatalogCode = (String) map.get("cInvCCode");
        String inventoryCode = (String) map.get("strInvoiceID");

        String startDate = (String) map.get("dtStartDate");
        String endDate = (String) map.get("dtEndDate");

        String memo = (String) map.get("strMemo");

        if (!Hibernate.isInitialized(contract)) {
            contract = getContractService().findById(contract.getId());
            item.setContract(contract);
        }
        boolean modified = false;

        holder.debug("条目：" + title + " x " + amount);
        if (taxRate >= TaxRateUtils.TAX_RATE_GENERAL_OLDEST
                && TaxRateUtils.isAfterTaxRateChangeTo13(contract.getSetupDate())) {
            holder.debug("税率 " + taxRate + "% 调整为 13%");
            taxRate = TaxRateUtils.TAX_RATE_GENERAL;
            double exclusiveTax = TaxRateUtils.toExclusiveTax(taxPrice, taxRate);
            holder.debug("不含税单价 " + exclusiveTaxPrice + " 调整为 " + exclusiveTax);
            exclusiveTaxPrice = exclusiveTax;
        }
        if (updateText(item::getTitle, item::setTitle, title, holder, "名称")) {
            modified = true;
        }
        if (updateText(item::getSpecification, item::setSpecification, spec, holder, "规格")) {
            modified = true;
        }
        if (updateText(item::getItemCode, item::setItemCode, code, holder, "代码")) {
            modified = true;
        }
        if (updateText(item::getUnit, item::setUnit, unit, holder, "单位")) {
            modified = true;
        }
        if (updateNumber(item::getExclusiveTaxPrice, item::setExclusiveTaxPrice, exclusiveTaxPrice, holder, "不含税单价")) {
            modified = true;
        }
        if (updateNumber(item::getTaxPrice, item::setTaxPrice, taxPrice, holder, "含税单价")) {
            modified = true;
        }
        if (updateNumber(item::getTaxRate, item::setTaxRate, taxRate, holder, "税率")) {
            modified = true;
        }
        if (updateNumber(item::getQuantity, item::setQuantity, amount, holder, "数量")) {
            modified = true;
        }
        if (getInventoryCtx().syncInventoryDetailByCode(item::getInventory, item::setInventory, inventoryCode,
                holder.sub("存货 #" + inventoryCode + " - "), "存货")) {
            modified = true;
        }

        {
            LocalDate date = null;

            if (StringUtils.hasText(startDate)) {
                try {
                    date = LocalDate.parse(startDate);
                } catch (DateTimeParseException e) {
                    holder.warn("解析日期" + startDate + " 异常：" + e.getMessage());
                }
            }
            if (!Objects.equals(item.getStartDate(), date)) {
                item.setStartDate(date);
                holder.info("开始日期修改为: " + date);
                modified = true;
            }
        }

        {
            LocalDate date = null;
            if (StringUtils.hasText(endDate)) {
                try {
                    date = LocalDate.parse(endDate);
                } catch (DateTimeParseException e) {
                    holder.warn("解析日期" + startDate + " 异常：" + e.getMessage());
                }
            }
            if (!Objects.equals(item.getEndDate(), date)) {
                item.setEndDate(date);
                holder.info("结束日期修改为: " + date);
                modified = true;
            }
        }

        if (!Objects.equals(item.getRemark(), memo)) {
            item.setRemark(memo);
            modified = true;
        }

        return modified;
    }

    private boolean sumItems(Contract contract, Collection<ContractItem> items, MessageHolder holder) {
        if (items == null || items.isEmpty()) {
            return false;
        }
        boolean modified = false;

        double totalQuantity = items.stream().mapToDouble(ContractItem::getQuantity).sum();
        double totalAmount = items.stream().mapToDouble(item -> item.getTaxPrice() * item.getQuantity()).sum();
        double totalUnTaxAmount = items.stream().mapToDouble(item -> item.getExclusiveTaxPrice() * item.getQuantity())
                .sum();

        if (updateNumber(contract::getTotalQuantity, contract::setTotalQuantity, totalQuantity, holder, "总数量")) {
            modified = true;
        }
        if (updateNumber(contract::getTotalAmount, contract::setTotalAmount, totalAmount, holder, "总金额")) {
            modified = true;
        }
        if (updateNumber(contract::getTotalUnTaxAmount, contract::setTotalUnTaxAmount, totalUnTaxAmount, holder,
                "总不含税金额")) {
            modified = true;
        }
        return modified;
    }

    public boolean updateContractPath(Contract contract, MessageHolder holder) {
        // 如果合同路径存在
        if (CompanyFileUtils.exists(contract.getPath())) {
            File dir = new File(contract.getPath());
            if (dir.exists()) {
                return false;
            }
        }
        // 尝试创建合同路径
        holder.debug("合同目录不存在，尝试创建");

        if (contract.getPayWay() == ContractPayWay.PAY) {
            // 采购合同
            String parentCode = contract.getParentCode();
            if (!StringUtils.hasText(parentCode)) {
                holder.warn("未关联主合同编号，采购合同目录创建在销售合同目录中，需先关联销售合同!");
                return false;
            }
            Contract parent = getContractService().findByCode(parentCode);
            if (parent == null) {
                holder.warn("关联主合同编号 " + parentCode + " 异常无法找到对应的合同，请检查主合同编号是否正确，或稍后再试!");
                return false;
            }
            if (!StringUtils.hasText(parent.getPath())) {
                holder.warn("关联主合同编号 " + parent.getCode() + " 未创建目录，请先创建主合同目录，稍后再试!");
                return false;
            }
            File contractPath = new File(parent.getPath());
            if (!contractPath.exists()) {
                holder.debug("父合同 " + parentCode + " 目录不存在 " + parent.getPath());
                return false;
            }
            String contractDirName = contract.getCode();
            File path = new File(contractPath, contractDirName);
            if (!path.exists()) {
                if (!path.mkdir()) {
                    holder.warn("创建目录失败 = " + path.getAbsolutePath());
                    return false;
                }
            }
            contract.setPath(path.getAbsolutePath());
            holder.info("目录变更为:" + contract.getPath());
            return true;
        } else if (contract.getPayWay() == ContractPayWay.RECEIVE) {
            // 销售合同
            File contractPath = null;
            Project project = contract.getProject();
            if (project != null) {
                project = getProjectCtx().initialize(project);
                ProjectSaleType saleType = project.getSaleType();
                if (saleType != null) {
                    if (!Hibernate.isInitialized(saleType)) {
                        saleType = getSaleTypeService().findById(saleType.getId());
                    }
                    File dir = new File(saleType.getPath());
                    if (saleType.isStoreByYear()) {
                        dir = new File(dir, "20" + project.getCodeYear());
                        if (!dir.exists()) {
                            if (dir.mkdir()) {
                                holder.info("新建目录 " + dir.getAbsolutePath());
                            }
                        }
                    }
                    contractPath = dir;
                }
            }

            // 从关联项目上未取得合同目录，尝试根据合同编码来确定合同目录
            if (contractPath == null) {
                ContractCatalog catalog = getContractService().findContractCatalogByContract(contract);
                if (catalog != null) {
                    contractPath = getContractService().getContractCatalogPath(catalog, contract);
                }
            }

            // 没有定位到合同目录
            if (contractPath == null) {
                return false;
            }

            String contractDirName = contract.getCode() + "-" + CompanyFileUtils.escapeFileName(contract.getName());
            File path = new File(contractPath, contractDirName);
            if (!path.exists()) {
                if (!path.mkdir()) {
                    //
                    holder.warn("创建目录失败 = " + path.getAbsolutePath());
                    return false;
                }
            }
            contract.setPath(path.getAbsolutePath());
            holder.info("目录变更为:" + contract.getPath());
            return true;
        }
        return false;
    }

    public boolean updateContractAmount(Contract contract, MessageHolder holder) {
        if (contract.getAmount() == null || contract.getAmount() == 0.0d) {
            if (contract.getTotalAmount() > 0) {
                contract.setAmount(contract.getTotalAmount());
                holder.info("合同金额设置为：" + contract.getAmount());
                return true;
            }
        }
        return false;
    }

    public boolean updateContractProject(Contract contract, Project project, MessageHolder holder) {
        if (project == null) {
            return false;
        }
        if (Objects.equals(contract.getProject(), project)) {
            return false;
        }
        project = getProjectCtx().initialize(project);
        holder.info("合同所属项目更新为：" + project.getName());
        contract.setProject(project);
        return true;
    }

    /**
     * 根据销售合同的客户，更新项目的客户
     *
     * @param contract
     * @param holder
     * @return
     */
    public boolean updateContractProject(Contract contract, Contract parent, MessageHolder holder) {
        Project project = contract.getProject();
        if (project == null && parent != null) {
            // 尝试从父合同中获取项目对象
            project = parent.getProject();
        }

        if (project == null) {
            return false;
        }

        boolean projectModified = false;
        boolean contractModified = false;

        ProjectCtx ctx = getProjectCtx();

        project = ctx.initialize(project);
        if (project.getAmount() == null || project.getAmount() == 0) {
            if (ctx.updateNumber(project::getAmount, project::setAmount, contract.getAmount().intValue(), holder,
                    "项目金额")) {
                projectModified = true;
            }
        }

        // 检查项目的客户
        Company customer = project.getCustomer();
        if (customer == null) {
            // 查询合同关联的客户（主合同上的是客户）
            if (parent == null) { // 主合同时，parent 为 null
                if (contract.getPayWay() == ContractPayWay.RECEIVE) {
                    customer = contract.getCompany();
                }
            } else {
                customer = parent.getCompany();
                if (customer == null) {
                    holder.warn("合同：" + parent.getCode() + " 没有关联的客户");
                }
            }
            if (customer != null) {
                if (ctx.updateCustomer(project, customer, holder)) {
                    projectModified = true;
                }
            }
        }

        if (projectModified) {
            project = ctx.save(project);
            contract.setProject(project);
        }

        if (updateContractProject(contract, project, holder)) {
            // 更新项目客户
            contractModified = true;
        }

        return contractModified;
    }

    public boolean syncContractFiles(Contract contract, MessageHolder holder) {
        String contractPath = contract.getPath();
        if (!StringUtils.hasText(contractPath)) {
            return false;
        }
        File dir = new File(contractPath);
        if (!dir.exists()) {
            holder.warn("合同目录不存在：" + contractPath);
            return false;
        }
        ContractFileService fileService = getContractFileService();
        List<ContractFile> dbFiles = fileService.findAllByContract(contract);
        List<ContractFile> retrieveFiles = new ArrayList<>();
        boolean modfied = false;

        Map<String, ContractFile> map = new HashMap<>();
        // 排除掉数据库中重复的
        for (ContractFile dbFile : dbFiles) {
            String fileName = dbFile.getFileName();
            // 没有文件信息，无效记录，删除
            if (!StringUtils.hasText(fileName)) {
                fileService.delete(dbFile);
                modfied = true;
                continue;
            }

            // 目录不存在，删除
            File file = new File(dir, fileName);
            if (!file.exists()) {
                fileService.delete(dbFile);
                modfied = true;
                continue;
            }

            // 删除文件名重复的
            ContractFile old = map.put(fileName, dbFile);
            if (old != null) {
                fileService.delete(old);
                modfied = true;
            }
        }

        // 遍历合同目录下的文件，如果未创建，创建
        File[] files = dir.listFiles();
        if (files != null) {
            for (File file : files) {
                // 只处理文件
                if (!file.isFile()) {
                    continue;
                }
                String fileName = file.getName();
                if (map.containsKey(fileName)) {
                    continue;
                }
                // 未记录的文件，存储到 retrieveFiles
                ContractFile contractFile = new ContractFile();
                contractFile.setContract(contract);
                contractFile.setType(ContractFileType.General);
                contractFile.setFileName(file.getName());
                syncContractFile(contractFile, file, holder);
                retrieveFiles.add(contractFile);
            }
        }

        if (retrieveFiles.isEmpty()) {
            return modfied;
        }

        // update db
        for (ContractFile retrieveFile : retrieveFiles) {
            getContractFileService().save(retrieveFile);
        }
        holder.info("导入 " + retrieveFiles.size() + " 个文件");
        return true;
    }

    /**
     * 把文件同步保存到合同
     *
     * @param contractFile
     * @param file
     * @param holder
     */
    public void syncContractFile(ContractFile contractFile, File file, MessageHolder holder) {
        ContractPayWay payWay = contractFile.getContract().getPayWay();

        String fileName = file.getName();

        updateText(contractFile::getFileName, contractFile::setFileName, file.getName(), holder, "文件名");
        if (contractFile.getApplyDate() == null) {
            updateLocalDate(contractFile::getApplyDate, contractFile::setApplyDate,
                    MyDateTimeUtils.pickLocalDate(file.getName()), holder, "生效日期");
        }

        List<ContractFileTypeLocal> matched = getContractFileService()
                .findAllFileTypes(Locale.getDefault().toLanguageTag()).values().stream()
                .filter(local -> {
                    if (payWay == ContractPayWay.PAY) {
                        return local.getType().isSupportVendor();
                    } else if (payWay == ContractPayWay.RECEIVE) {
                        return local.getType().isSupportCustomer();
                    }
                    return false;
                }).filter(local -> {
                    String suggestFileName = local.getSuggestFileName();
                    if (StringUtils.hasText(suggestFileName)) {
                        if (suggestFileName.contains("{}")) {
                            // TODO 匹配 {}
                        }
                        if (fileName.contains(suggestFileName)) {
                            return true;
                        }
                    }
                    return fileName.contains(local.getValue());
                }).toList();

        // 没有匹配，不做处理
        if (matched.isEmpty()) {
            return;
        }

        if (matched.size() == 1) {
            contractFile.setType(matched.getFirst().getType());
            return;
        }
        contractFile.setType(matched.stream().max((o1, o2) -> maxLength(o1) - maxLength(o2)).get().getType());
    }

    private int maxLength(ContractFileTypeLocal o1) {
        String suggestFileName = o1.getSuggestFileName();
        String value = o1.getValue();
        if (!StringUtils.hasText(suggestFileName)) {
            return value.length();
        }
        return Math.max(suggestFileName.length(), value.length());
    }

    public Contract save(Contract contract) {
        return getContractService().save(contract);
    }

    public boolean syncContractPayPlan(Contract contract, MessageHolder holder) {
        if (repository == null) {
            return false;
        }
        holder.debug("- 同步合同付款计划 -");
        ContractPayPlanService payPlanService = getContractPayPlanService();
        boolean changed = false;

        List<ContractPayPlan> plans = payPlanService.findAllByContract(contract);
        holder.debug("查找到 " + plans.size() + " 条合同付款计划记录在数据库中");

        Map<Integer, ContractPayPlan> planMap = plans.stream()
                .collect(Collectors.toMap(ContractPayPlan::getRefId, item -> item));
        List<Map<String, Object>> ds = repository.findAllContractPayPlanByContractCode(contract.getCode());
        holder.debug("查找到 " + ds.size() + " 条合同付款计划记录在 " + CloudServiceConstant.U8_NAME);

        List<ContractPayPlan> updates = new ArrayList<>();
        for (Map<String, Object> map : ds) {
            // holder.debug("条目：" + map.toString());
            int refId = (Integer) map.get("ID");
            ContractPayPlan plan = planMap.remove(refId);
            boolean modified = false;
            if (plan == null) {
                plan = new ContractPayPlan();
                plan.setContract(contract);
                plan.setRefId(refId);
                modified = true;

                holder.info("新增合同付款计划");
            }
            MessageHolder subHolder = holder.sub("--| #" + refId + " - ");
            if (applyContractPayPlanDetail(plan, map, subHolder)) {
                modified = true;
            }
            if (modified) {
                plan.setUpdateDate(LocalDateTime.now());
                plan = payPlanService.save(plan);
                changed = true;
            }
            updates.add(plan);
        }
        if (!planMap.isEmpty()) {
            for (ContractPayPlan plan : planMap.values()) {
                payPlanService.delete(plan);
            }
            changed = true;
        }

        double totalRatio = updates.stream().mapToDouble(ContractPayPlan::getPayRatio).sum();
        if (!NumberUtils.equals(totalRatio, 100.0)) {
            holder.error("合同付款计划合计比例 != 100");
        }
        double totalCurrency = updates.stream().mapToDouble(ContractPayPlan::getPayCurrency).sum();
        if (!NumberUtils.equals(totalCurrency, contract.getAmount())) {
            NumberFormat numberInstance = NumberFormat.getNumberInstance(getLocale());
            numberInstance.setMaximumFractionDigits(2);
            numberInstance.setMinimumFractionDigits(2);
            holder.error("合同付款计划合计金额（" + numberInstance.format(totalCurrency) + "）和合同金额（"
                    + numberInstance.format(contract.getAmount()) + "）不一致!");
        }
        return changed;
    }

    private boolean applyContractPayPlanDetail(ContractPayPlan plan, Map<String, Object> map, MessageHolder holder) {
        boolean modified = false;
        String payDate = (String) map.get("dtPayDate");
        double payRatio = (double) map.get("dblPayRatio");
        double payCurrency = (double) map.get("dblPayCurrency");
        String payTerm = (String) map.get("strExp");

        if (updateLocalDate(plan::getPayDate, plan::setPayDate, payDate, holder, "付款日期")) {
            modified = true;
        }
        if (updateNumber(plan::getPayRatio, plan::setPayRatio, (float) payRatio, holder, "付款比例")) {
            modified = true;
        }
        if (updateNumber(plan::getPayCurrency, plan::setPayCurrency, payCurrency, holder, "付款金额")) {
            modified = true;
        }
        if (updateText(plan::getPayTerm, plan::setPayTerm, payTerm, holder, "付款条款")) {
            modified = true;
        }
        return modified;
    }
}
