package org.qinan.safetyeval.app.executor;

import org.qinan.safetyeval.app.change.convertor.*;
import org.qinan.safetyeval.app.orchestrator.QualFilingOrchestrator;
import org.qinan.safetyeval.client.api.QualFilingChangeApi;
import org.qinan.safetyeval.client.co.QualFilingChangeAggregationCO;
import org.qinan.safetyeval.client.co.QualFilingChangeCO;
import org.qinan.safetyeval.client.co.QualFilingChangeComplianceCheckResultCO;
import org.qinan.safetyeval.client.co.QualFilingChangeHistoryCO;
import org.qinan.safetyeval.client.dto.*;
import org.qinan.safetyeval.domain.change.gateway.*;
import org.qinan.safetyeval.domain.change.model.*;
import org.qinan.safetyeval.domain.constant.QualFilingFormStepEnum;
import org.qinan.safetyeval.domain.constant.QualFilingReviewResultEnum;
import org.qinan.safetyeval.domain.constant.QualFilingStatusEnum;
import org.qinan.safetyeval.domain.entity.*;
import org.qinan.safetyeval.domain.exception.BizException;
import org.qinan.safetyeval.domain.exception.ErrorCode;
import org.qinan.safetyeval.domain.gateway.QualFilingChangeDetailGateway;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.domain.query.QualFilingChangeQuery;
import org.qinan.safetyeval.domain.service.QualFilingChangeDomainService;
import org.qinan.safetyeval.domain.service.QualFilingDomainService;
import org.qinan.safetyeval.domain.service.QualFilingReviewDomainService;
import org.qinan.safetyeval.infrastructure.adapter.auth.AuthUserContextAdapter;
import org.qinan.safetyeval.infrastructure.utils.RequestHandlerUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 备案变更执行器（App层）
 */
@Service
public class QualFilingChangeExecutor implements QualFilingChangeApi {

    @Lazy
    @Autowired(required = false)
    private QualFilingChangeDomainService qualFilingChangeDomainService;

    @Lazy
    @Autowired(required = false)
    private QualFilingDomainService qualFilingDomainService;

    @Lazy
    @Autowired(required = false)
    private QualFilingReviewDomainService qualFilingReviewDomainService;

    @Resource
    private QualFilingOrchestrator qualFilingOrchestrator;
    @Resource
    private QualFilingChangeCmdConvertor qualFilingChangeCmdConvertor;
    @Resource
    private QualFilingCommitmentChangeCmdConvertor qualFilingCommitmentChangeCmdConvertor;
    @Resource
    private QualFilingEquipmentChangeCmdConvertor qualFilingEquipmentChangeCmdConvertor;
    @Resource
    private QualFilingMaterialChangeCmdConvertor qualFilingMaterialChangeCmdConvertor;
    @Resource
    private QualFilingPersonnelChangeCmdConvertor qualFilingPersonnelChangeCmdConvertor;
    @Resource
    private QualFilingPersonnelCertChangeCmdConvertor qualFilingPersonnelCertChangeCmdConvertor;

    @Resource
    private QualFilingCommitmentChangeGateway qualFilingCommitmentChangeGateway;
    @Resource
    private QualFilingEquipmentChangeGateway qualFilingEquipmentChangeGateway;
    @Resource
    private QualFilingMaterialChangeGateway qualFilingMaterialChangeGateway;
    @Resource
    private QualFilingPersonnelChangeGateway qualFilingPersonnelChangeGateway;
    @Resource
    private QualFilingPersonnelCertChangeGateway qualFilingPersonnelCertChangeGateway;

    @Resource
    private QualFilingChangeDetailGateway qualFilingChangeDetailGateway;

    @Override
    public SingleResponse<QualFilingChangeCO> add(QualFilingChangeAddCmd cmd) {
        QualFilingChangeEntity entity = new QualFilingChangeEntity();
        entity.setFilingId(cmd.getFilingId());
        entity.setFilingTerritoryCode(cmd.getFilingTerritoryCode());
        entity.setFilingTerritoryName(cmd.getFilingTerritoryName());
        entity.setFilingUnitName(cmd.getFilingUnitName());
        entity.setFilingUnitTypeCode(cmd.getFilingUnitTypeCode());
        entity.setFilingUnitTypeName(cmd.getFilingUnitTypeName());
        entity.setFilingNo(cmd.getFilingNo());
        entity.setBusinessScope(cmd.getBusinessScope());
        entity.setRegisterAddress(cmd.getRegisterAddress());
        entity.setOfficeAddress(cmd.getOfficeAddress());
        entity.setCreditCode(cmd.getCreditCode());
        entity.setQualCertNo(cmd.getQualCertNo());
        entity.setLegalPersonPhone(cmd.getLegalPersonPhone());
        entity.setContactPhone(cmd.getContactPhone());
        entity.setInfoDisclosureUrl(cmd.getInfoDisclosureUrl());
        entity.setFixedAssetAmount(cmd.getFixedAssetAmount());
        entity.setArchiveRoomArea(cmd.getArchiveRoomArea());
        entity.setFulltimeEvaluatorCount(cmd.getFulltimeEvaluatorCount());
        entity.setRegisteredEngineerCount(cmd.getRegisteredEngineerCount());
        entity.setWorkplaceArea(cmd.getWorkplaceArea());
        entity.setUnitIntro(cmd.getUnitIntro());
        entity.setAttachmentUrl(cmd.getAttachmentUrl());
        entity.setFilingStatusCode(cmd.getFilingStatusCode());
        entity.setFilingStatusName(cmd.getFilingStatusName());
        QualFilingChangeEntity result = qualFilingChangeDomainService.add(entity);
        return SingleResponse.success(toCO(result));
    }

    @Override
    public SingleResponse<QualFilingChangeCO> get(Long id) {
        QualFilingChangeEntity entity = qualFilingChangeDomainService.get(id);
        return SingleResponse.success(toCO(entity));
    }

    @Override
    public SingleResponse<QualFilingChangeCO> modify(QualFilingChangeModifyCmd cmd) {
        QualFilingChangeEntity entity = new QualFilingChangeEntity();
        entity.setId(cmd.getId());
        entity.setFilingId(cmd.getFilingId());
        entity.setFilingTerritoryCode(cmd.getFilingTerritoryCode());
        entity.setFilingTerritoryName(cmd.getFilingTerritoryName());
        entity.setFilingUnitName(cmd.getFilingUnitName());
        entity.setFilingUnitTypeCode(cmd.getFilingUnitTypeCode());
        entity.setFilingUnitTypeName(cmd.getFilingUnitTypeName());
        entity.setFilingNo(cmd.getFilingNo());
        entity.setBusinessScope(cmd.getBusinessScope());
        entity.setRegisterAddress(cmd.getRegisterAddress());
        entity.setOfficeAddress(cmd.getOfficeAddress());
        entity.setCreditCode(cmd.getCreditCode());
        entity.setQualCertNo(cmd.getQualCertNo());
        entity.setLegalPersonPhone(cmd.getLegalPersonPhone());
        entity.setContactPhone(cmd.getContactPhone());
        entity.setInfoDisclosureUrl(cmd.getInfoDisclosureUrl());
        entity.setFixedAssetAmount(cmd.getFixedAssetAmount());
        entity.setArchiveRoomArea(cmd.getArchiveRoomArea());
        entity.setFulltimeEvaluatorCount(cmd.getFulltimeEvaluatorCount());
        entity.setRegisteredEngineerCount(cmd.getRegisteredEngineerCount());
        entity.setWorkplaceArea(cmd.getWorkplaceArea());
        entity.setUnitIntro(cmd.getUnitIntro());
        entity.setAttachmentUrl(cmd.getAttachmentUrl());
        entity.setFilingStatusCode(cmd.getFilingStatusCode());
        entity.setFilingStatusName(cmd.getFilingStatusName());
        QualFilingChangeEntity result = qualFilingChangeDomainService.modify(entity);
        return SingleResponse.success(toCO(result));
    }

    @Override
    public SingleResponse<Void> delete(Long id) {
        qualFilingChangeDomainService.delete(id);
        return SingleResponse.success();
    }

    @Override
    public PageResponse<QualFilingChangeCO> page(QualFilingChangePageQuery query) {
        QualFilingChangeQuery domainQuery = new QualFilingChangeQuery();
        domainQuery.setPageNum(query.getCurrent());
        domainQuery.setPageSize(query.getSize());
        domainQuery.setTenantId(AuthUserContextAdapter.getCurrentTenantId());
        domainQuery.setOrgId(query.getOrgId());
        domainQuery.setFilingNo(query.getFilingNo());
        domainQuery.setFilingStatusCode(query.getFilingStatusCode());
        PageResult<QualFilingChangeEntity> pageResult = qualFilingChangeDomainService.page(domainQuery);
        return PageResponse.of(
                pageResult.getRecords().stream().map(this::toCO).collect(java.util.stream.Collectors.toList()),
                pageResult.getTotal());
    }

    @Override
    public SingleResponse<Long> start(Long filingId) {
        return SingleResponse.success(qualFilingOrchestrator.startChange(filingId));
    }

    @Override
    public SingleResponse<Void> submit(QualFilingChangeSubmitCmd cmd) {
        qualFilingOrchestrator.submitChange(cmd.getDraftFilingId());
        return SingleResponse.success();
    }

    @Override
    public SingleResponse<QualFilingChangeHistoryCO> history(Long filingId) {
        return SingleResponse.success(qualFilingOrchestrator.getChangeHistory(filingId));
    }

    @Override
    public SingleResponse<Void> approve(Long changeId) {
        qualFilingOrchestrator.approveChange(changeId);
        return SingleResponse.success();
    }

    @Override
    public SingleResponse<Void> reject(QualFilingRejectCmd cmd) {
        qualFilingOrchestrator.rejectChange(cmd.getId(), cmd.getRejectReason());
        return SingleResponse.success();
    }

    @Override
    public SingleResponse<List<QualFilingChangeComplianceCheckResultCO>> complianceCheck(Long id) {
        return SingleResponse.success(qualFilingOrchestrator.complianceCheckChange(id));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public SingleResponse<QualFilingChangeCO> aggregationSaveOrEdit(AggregationSaveOrEditQualFilingChangeCmd cmd) {
        validateBeforeSaveOrEdit(cmd);

        // 变更基本信息
        QualFilingChangeEntity entity = qualFilingChangeCmdConvertor.convertCmdToE(cmd.getQualFilingChangeAddCmd());
        entity.setOrgId(RequestHandlerUtil.getOrgId());

        // 变更承诺书
        QualFilingCommitmentChangeE commitmentEntity = qualFilingCommitmentChangeCmdConvertor.convertCmdToE(cmd.getQualFilingCommitmentChangeAddCmd());

        // 变更装备
        List<QualFilingEquipmentChangeE> equipmentEntities = cmd.getQualFilingEquipmentChangeAddCmds().stream()
                .map(item -> qualFilingEquipmentChangeCmdConvertor.convertCmdToE(item))
                .collect(Collectors.toList());

        // 变更材料
        List<QualFilingMaterialChangeE> materialEntities = cmd.getQualFilingMaterialChangeAddCmds().stream()
                .map(item -> qualFilingMaterialChangeCmdConvertor.convertCmdToE(item))
                .collect(Collectors.toList());

        // 变更人员
        List<QualFilingPersonnelChangeE> personnelEntities = cmd.getQualFilingPersonnelChangeAddCmds().stream()
                .map(item -> qualFilingPersonnelChangeCmdConvertor.convertCmdToE(item))
                .collect(Collectors.toList());

        // 变更人员证书
        List<QualFilingPersonnelCertChangeE> personnelCertEntities = Collections.emptyList();
        if (!CollectionUtils.isEmpty(cmd.getQualFilingPersonnelCertChangeAddCmds())) {
            personnelCertEntities = cmd.getQualFilingPersonnelCertChangeAddCmds().stream()
                    .map(item -> qualFilingPersonnelCertChangeCmdConvertor.convertCmdToE(item))
                    .collect(Collectors.toList());
        }

        // 编辑场景：对比请求数据与已有变更数据，生成变更明细
        if (!cmd.isAdd()) {
            Long changeId = cmd.getQualFilingChangeAddCmd().getId();
            List<QualFilingChangeDetailEntity> details = diffFromCmd(changeId
                    , entity, commitmentEntity, equipmentEntities
                    , materialEntities, personnelEntities, personnelCertEntities);
            // 根据changeId先删除原有变更明细，再新增
            List<QualFilingChangeDetailEntity> existingDetails = qualFilingChangeDetailGateway.listByChangeId(changeId);
            if (!CollectionUtils.isEmpty(existingDetails)) {
                existingDetails.forEach(d -> qualFilingChangeDetailGateway.delete(d.getId()));
            }
            if (!CollectionUtils.isEmpty(details)) {
                String operatorName = AuthUserContextAdapter.getCurrentUserName();
                details.forEach(d -> {
                    d.setCreateName(operatorName);
                    qualFilingChangeDetailGateway.save(d);
                });
            }
        }

        Long id = qualFilingChangeDomainService.aggregationSaveOrEdit(entity, commitmentEntity
                , equipmentEntities, materialEntities, personnelEntities, personnelCertEntities, cmd.isAdd());

        QualFilingChangeCO co = new QualFilingChangeCO();
        co.setId(id);
        return SingleResponse.success(co);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void review(QualFilingChangeReviewAddCmd cmd) {
        Long changeFilingId = cmd.getId();

        // 查询变更基本信息
        QualFilingChangeEntity changeEntity = qualFilingChangeDomainService.get(changeFilingId);

        if (changeEntity == null) {
            throw new BizException(ErrorCode.QUAL_FILING_CHANGE_NOT_FOUND);
        }
        if (QualFilingStatusEnum.REVIEWING.getCode() != changeEntity.getFilingStatusCode()) {
            throw new BizException(ErrorCode.STATUS_NOT_APPROVED);
        }

        QualFilingChangeEntity updateE = qualFilingChangeDomainService.get(changeFilingId);
        updateE.setId(changeFilingId);

        // 构建并保存审核记录
        QualFilingReviewEntity reviewEntity = new QualFilingReviewEntity();
        reviewEntity.setFilingId(changeEntity.getChangeFilingId());
        reviewEntity.setReviewResult(cmd.getReviewResult());
        reviewEntity.setFilingExpertId(cmd.getFilingExpertId());
        reviewEntity.setFilingExpertName(cmd.getFilingExpertName());
        reviewEntity.setReviewOpinion(cmd.getReviewOpinion());
        reviewEntity = qualFilingReviewDomainService.add(reviewEntity);

        // 更新变更记录的 filing_review_id 和 filing_status_code
        updateE.setFilingReviewId(reviewEntity.getId());

        // 审核通过：将变更数据同步到正式备案表
        if (String.valueOf(QualFilingReviewResultEnum.PASS.getCode()).equals(cmd.getReviewResult())) {
            updateE.setFilingStatusCode(QualFilingStatusEnum.FILED.getCode());
            updateE.setFilingStatusName(QualFilingStatusEnum.FILED.getName());

            // 转换变更基本信息 → 备案实体
            QualFilingEntity entity = qualFilingChangeCmdConvertor.convertChangeEToEntity(changeEntity);
            entity.setId(changeEntity.getChangeFilingId());

            // 查询并转换承诺书
            QualFilingCommitmentChangeE commitmentChangeE = qualFilingCommitmentChangeGateway.getByFilingId(changeFilingId);
            QualFilingCommitmentEntity commitmentEntity = null;
            if (commitmentChangeE != null) {
                commitmentEntity = qualFilingCommitmentChangeCmdConvertor.convertChangeEToEntity(commitmentChangeE);
                commitmentEntity.setFilingId(changeEntity.getChangeFilingId());
            }

            // 查询并转换装备清单
            List<QualFilingEquipmentChangeE> equipmentChangeEs = qualFilingEquipmentChangeGateway.listByFilingId(changeFilingId);
            List<QualFilingEquipmentEntity> equipmentEntities = qualFilingEquipmentChangeCmdConvertor.convertChangeEListToEntityList(equipmentChangeEs);
            equipmentEntities.forEach(e -> e.setFilingId(changeEntity.getChangeFilingId()));

            // 查询并转换材料清单
            List<QualFilingMaterialChangeE> materialChangeEs = qualFilingMaterialChangeGateway.listByFilingId(changeFilingId);
            List<QualFilingMaterialEntity> materialEntities = qualFilingMaterialChangeCmdConvertor.convertChangeEListToEntityList(materialChangeEs);
            materialEntities.forEach(e -> e.setFilingId(changeEntity.getChangeFilingId()));

            // 查询并转换人员
            List<QualFilingPersonnelChangeE> personnelChangeEs = qualFilingPersonnelChangeGateway.listByFilingId(changeFilingId);
            List<QualFilingPersonnelEntity> personnelEntities = qualFilingPersonnelChangeCmdConvertor.convertChangeEListToEntityList(personnelChangeEs);
            personnelEntities.forEach(e -> e.setFilingId(changeEntity.getChangeFilingId()));

            // 查询并转换人员证书
            List<QualFilingPersonnelCertChangeE> personnelCertChangeEs = qualFilingPersonnelCertChangeGateway.listByFilingId(changeFilingId);
            List<QualFilingPersonnelCertEntity> personnelCertEntities = qualFilingPersonnelCertChangeCmdConvertor.convertChangeEListToEntityList(personnelCertChangeEs);
            personnelCertEntities.forEach(e -> e.setFilingId(changeEntity.getChangeFilingId()));

            // 调用聚合保存，将变更数据写入正式备案表
            qualFilingDomainService.aggregationSaveOrEdit(entity, commitmentEntity, equipmentEntities, materialEntities, personnelEntities, personnelCertEntities);
        } else {
            updateE.setFilingStatusCode(QualFilingStatusEnum.REJECTED.getCode());
            updateE.setFilingStatusName(QualFilingStatusEnum.REJECTED.getName());
        }

        // 更新变更记录
        qualFilingChangeDomainService.modify(updateE);
    }

    @Override
    public SingleResponse<QualFilingChangeAggregationCO> detail(Long id) {
        // 查询变更主记录
        QualFilingChangeEntity changeEntity = qualFilingChangeDomainService.get(id);
        if (changeEntity == null) {
            throw new BizException(ErrorCode.QUAL_FILING_CHANGE_NOT_FOUND);
        }

        QualFilingChangeAggregationCO detail = qualFilingChangeCmdConvertor
                .convertChangeEToQualFilingChangeAggregationCO(changeEntity);
        Long changeFilingId = changeEntity.getId();

        // 承诺书
        QualFilingCommitmentChangeE commitmentChangeE = qualFilingCommitmentChangeGateway.getByFilingId(changeFilingId);
        if (commitmentChangeE != null) {
            detail.setCommitment(qualFilingCommitmentChangeCmdConvertor.convertChangeEToCO(commitmentChangeE));
        }

        // 装备清单
        List<QualFilingEquipmentChangeE> equipmentChangeEs = qualFilingEquipmentChangeGateway.listByFilingId(changeFilingId);
        if (!CollectionUtils.isEmpty(equipmentChangeEs)) {
            detail.setEquipmentList(qualFilingEquipmentChangeCmdConvertor.convertChangeEListToCOList(equipmentChangeEs));
        }

        // 材料清单
        List<QualFilingMaterialChangeE> materialChangeEs = qualFilingMaterialChangeGateway.listByFilingId(changeFilingId);
        if (!CollectionUtils.isEmpty(materialChangeEs)) {
            detail.setMaterials(qualFilingMaterialChangeCmdConvertor.convertChangeEListToCOList(materialChangeEs));
        }

        // 人员清单
        List<QualFilingPersonnelChangeE> personnelChangeEs = qualFilingPersonnelChangeGateway.listByFilingId(changeFilingId);
        if (!CollectionUtils.isEmpty(personnelChangeEs)) {
            detail.setPersonnelList(qualFilingPersonnelChangeCmdConvertor.convertChangeEListToCOList(personnelChangeEs));
        }

        // 人员证书清单
        List<QualFilingPersonnelCertChangeE> personnelCertChangeEs = qualFilingPersonnelCertChangeGateway.listByFilingId(changeFilingId);
        if (!CollectionUtils.isEmpty(personnelCertChangeEs)) {
            detail.setPersonnelCertList(qualFilingPersonnelCertChangeCmdConvertor.convertChangeEListToCOList(personnelCertChangeEs));
        }

        return SingleResponse.success(detail);
    }

    /**
     * 聚合保存或编辑前的状态校验
     * <ul>
     *   <li>新增：根据filingId统计qualFilingChange表中filingStatusCode != 1(已备案)的记录数，有则不允许新增</li>
     *   <li>修改：根据id查询变更记录，filingStatusCode = 1(已备案) 则不允许修改</li>
     * </ul>
     */
    private void validateBeforeSaveOrEdit(AggregationSaveOrEditQualFilingChangeCmd cmd) {
        QualFilingChangeAddCmd addCmd = cmd.getQualFilingChangeAddCmd();
        if (addCmd.getId() == null) {
            cmd.setAdd(true);
            // 新增：根据filingId统计qualFilingChange表中filingStatusCode != 1的记录数，有则不允许新增
            int count = qualFilingChangeDomainService.countByFilingIdAndStatusNot(
                    addCmd.getFilingId(), QualFilingStatusEnum.FILED.getCode());
            if (count > 0) {
                throw new BizException(ErrorCode.QUAL_FILING_STATUS_INVALID, "存在进行中的备案变更，不允许新增");
            }
        } else {
            cmd.setAdd(false);
            // 修改：根据id查询变更记录，filing_status_code = 1 则不允许修改
            QualFilingChangeEntity existing = qualFilingChangeDomainService.get(addCmd.getId());
            if (existing == null) {
                throw new BizException(ErrorCode.QUAL_FILING_CHANGE_NOT_FOUND);
            }
            // 已备案的只能新增变更 否则就是修改
            if (QualFilingStatusEnum.isFiled(existing.getFilingStatusCode())) {
                // 新增：根据filingId统计qualFilingChange表中filingStatusCode != 1的记录数，有则不允许新增
                int count = qualFilingChangeDomainService.countByFilingIdAndStatusNot(
                        addCmd.getFilingId(), QualFilingStatusEnum.FILED.getCode());
                if (count > 0) {
                    throw new BizException(ErrorCode.QUAL_FILING_STATUS_INVALID, "存在进行中的备案变更，不允许新增");
                }
                cmd.setAdd(true);
                cmd.getQualFilingChangeAddCmd().setId(null);
                cmd.getQualFilingChangeAddCmd().setChangeFilingId(existing.getId());
                cmd.getQualFilingChangeAddCmd().setFilingId(existing.getFilingId());
            } else {
                cmd.setAdd(false);
            }
        }
    }

    /**
     * 对比请求数据与已有变更数据，生成变更明细列表
     */
    private List<QualFilingChangeDetailEntity> diffFromCmd(
            Long changeId, QualFilingChangeEntity newEntity, QualFilingCommitmentChangeE newCommitment,
            List<QualFilingEquipmentChangeE> newEquipments, List<QualFilingMaterialChangeE> newMaterials,
            List<QualFilingPersonnelChangeE> newPersonnel, List<QualFilingPersonnelCertChangeE> newCerts) {
        List<QualFilingChangeDetailEntity> details = new ArrayList<>();

        // 基本信息变更
        QualFilingChangeEntity existingEntity = qualFilingChangeDomainService.get(changeId);
        if (existingEntity != null && basicInfoChanged(existingEntity, newEntity)) {
            details.add(stepDetail(changeId, QualFilingFormStepEnum.BASIC_INFO, "备案基本信息变更",
                    summarizeBasic(existingEntity), summarizeBasic(newEntity)));
        }

        // 承诺书变更
        QualFilingCommitmentChangeE existingCommitment = qualFilingCommitmentChangeGateway.getByFilingId(changeId);
        if (commitmentChanged(existingCommitment, newCommitment)) {
            details.add(stepDetail(changeId, QualFilingFormStepEnum.COMMITMENT, "法定代表人承诺书变更",
                    "承诺书", "承诺书已变更"));
        }

        // 装备清单变更
        List<QualFilingEquipmentChangeE> existingEquipments = qualFilingEquipmentChangeGateway.listByFilingId(changeId);
        if (equipmentListChanged(existingEquipments, newEquipments)) {
            details.add(stepDetail(changeId, QualFilingFormStepEnum.EQUIPMENT, "装备清单变更",
                    "装备清单", "装备清单已变更"));
        }

        // 材料清单变更
        List<QualFilingMaterialChangeE> existingMaterials = qualFilingMaterialChangeGateway.listByFilingId(changeId);
        if (materialListChanged(existingMaterials, newMaterials)) {
            details.add(stepDetail(changeId, QualFilingFormStepEnum.MATERIALS, "备案材料上传变更",
                    "材料清单", "材料清单已变更"));
        }

        // 人员变更
        List<QualFilingPersonnelChangeE> existingPersonnel = qualFilingPersonnelChangeGateway.listByFilingId(changeId);
        if (personnelListChanged(existingPersonnel, newPersonnel)) {
            details.add(stepDetail(changeId, QualFilingFormStepEnum.PERSONNEL, "备案人员管理变更",
                    "人员清单", "人员清单已变更"));
        }

        // 人员证书变更
        List<QualFilingPersonnelCertChangeE> existingCerts = qualFilingPersonnelCertChangeGateway.listByFilingId(changeId);
        if (certListChanged(existingCerts, newCerts)) {
            details.add(stepDetail(changeId, QualFilingFormStepEnum.PERSONNEL, "备案人员证书变更",
                    "人员证书清单", "人员证书清单已变更"));
        }

        return details;
    }

    private boolean basicInfoChanged(QualFilingChangeEntity exist, QualFilingChangeEntity newEntity) {
        return !Objects.equals(exist.getFilingTerritoryCode(), newEntity.getFilingTerritoryCode())
                || !Objects.equals(exist.getBusinessScope(), newEntity.getBusinessScope())
                || !Objects.equals(exist.getRegisterAddress(), newEntity.getRegisterAddress())
                || !Objects.equals(exist.getOfficeAddress(), newEntity.getOfficeAddress())
                || !Objects.equals(exist.getCreditCode(), newEntity.getCreditCode())
                || !Objects.equals(exist.getQualCertNo(), newEntity.getQualCertNo())
                || !Objects.equals(exist.getLegalPersonPhone(), newEntity.getLegalPersonPhone())
                || !Objects.equals(exist.getContactPhone(), newEntity.getContactPhone())
                || !Objects.equals(exist.getInfoDisclosureUrl(), newEntity.getInfoDisclosureUrl())
                || !Objects.equals(exist.getFixedAssetAmount(), newEntity.getFixedAssetAmount())
                || !Objects.equals(exist.getArchiveRoomArea(), newEntity.getArchiveRoomArea())
                || !Objects.equals(exist.getFulltimeEvaluatorCount(), newEntity.getFulltimeEvaluatorCount())
                || !Objects.equals(exist.getRegisteredEngineerCount(), newEntity.getRegisteredEngineerCount())
                || !Objects.equals(exist.getWorkplaceArea(), newEntity.getWorkplaceArea())
                || !Objects.equals(exist.getUnitIntro(), newEntity.getUnitIntro())
                || !Objects.equals(exist.getAttachmentUrl(), newEntity.getAttachmentUrl());
    }

    private String summarizeBasic(QualFilingChangeEntity entity) {
        return entity.getFilingUnitName() + "|" + entity.getBusinessScope();
    }

    private boolean commitmentChanged(QualFilingCommitmentChangeE exist, QualFilingCommitmentChangeE newEntity) {
        String existSig = exist == null ? "" : exist.getCommitmentContent() + "|" + exist.getLegalRepSignatureUrl()
                                               + "|" + exist.getSignDate() + "|" + exist.getLegalRepPersonnelId() + "|" + exist.getLegalRepName();
        String newSig = newEntity == null ? "" : newEntity.getCommitmentContent() + "|" + newEntity.getLegalRepSignatureUrl()
                                                 + "|" + newEntity.getSignDate() + "|" + newEntity.getLegalRepPersonnelId() + "|" + newEntity.getLegalRepName();
        return !existSig.equals(newSig);
    }

    private boolean equipmentListChanged(List<QualFilingEquipmentChangeE> exist, List<QualFilingEquipmentChangeE> newList) {
        return !equipmentSignature(exist).equals(equipmentSignature(newList));
    }

    private String equipmentSignature(List<QualFilingEquipmentChangeE> list) {
        return safeList(list).stream()
                .map(e -> e.getSourceEquipmentId() + ":" + e.getCalibrationReportUrl())
                .sorted()
                .collect(Collectors.joining(";"));
    }

    private boolean materialListChanged(List<QualFilingMaterialChangeE> exist, List<QualFilingMaterialChangeE> newList) {
        return !materialSignature(exist).equals(materialSignature(newList));
    }

    private String materialSignature(List<QualFilingMaterialChangeE> list) {
        return safeList(list).stream()
                .map(m -> m.getMaterialContent() + ":" + m.getAttachmentUrl())
                .sorted()
                .collect(Collectors.joining(";"));
    }

    private boolean personnelListChanged(List<QualFilingPersonnelChangeE> exist, List<QualFilingPersonnelChangeE> newList) {
        return !personnelSignature(exist).equals(personnelSignature(newList));
    }

    private String personnelSignature(List<QualFilingPersonnelChangeE> list) {
        return safeList(list).stream()
                .map(QualFilingPersonnelChangeE::getSourcePersonnelId)
                .map(String::valueOf)
                .sorted()
                .collect(Collectors.joining(","));
    }

    private boolean certListChanged(List<QualFilingPersonnelCertChangeE> exist, List<QualFilingPersonnelCertChangeE> newList) {
        return !certSignature(exist).equals(certSignature(newList));
    }

    private String certSignature(List<QualFilingPersonnelCertChangeE> list) {
        return safeList(list).stream()
                .map(c -> c.getSourceCertId() + ":" + c.getCertNo() + ":" + c.getCertAttachmentUrl())
                .sorted()
                .collect(Collectors.joining(";"));
    }

    private <T> List<T> safeList(List<T> list) {
        return list == null ? Collections.emptyList() : list;
    }

    private QualFilingChangeDetailEntity stepDetail(Long changeId, QualFilingFormStepEnum step,
                                                    String content, String before, String after) {
        QualFilingChangeDetailEntity detail = new QualFilingChangeDetailEntity();
        detail.setChangeId(changeId);
        detail.setFormStepCode(step.getCode());
        detail.setFormStepName(step.getName());
        detail.setChangeField("step:" + step.getCode());
        detail.setChangeContent(content);
        detail.setChangeItemName(step.getName());
        detail.setBeforeValue(before);
        detail.setAfterValue(after);
        return detail;
    }

    private QualFilingChangeCO toCO(QualFilingChangeEntity entity) {
        if (entity == null) {
            return null;
        }
        QualFilingChangeCO co = new QualFilingChangeCO();
        co.setId(entity.getId());
        co.setFilingId(entity.getFilingId());
        co.setChangeFilingId(entity.getChangeFilingId());
        co.setFilingTerritoryCode(entity.getFilingTerritoryCode());
        co.setFilingTerritoryName(entity.getFilingTerritoryName());
        co.setFilingUnitName(entity.getFilingUnitName());
        co.setFilingUnitTypeCode(entity.getFilingUnitTypeCode());
        co.setFilingUnitTypeName(entity.getFilingUnitTypeName());
        co.setFilingNo(entity.getFilingNo());
        co.setBusinessScope(entity.getBusinessScope());
        co.setRegisterAddress(entity.getRegisterAddress());
        co.setOfficeAddress(entity.getOfficeAddress());
        co.setCreditCode(entity.getCreditCode());
        co.setQualCertNo(entity.getQualCertNo());
        co.setLegalPersonPhone(entity.getLegalPersonPhone());
        co.setContactPhone(entity.getContactPhone());
        co.setInfoDisclosureUrl(entity.getInfoDisclosureUrl());
        co.setFixedAssetAmount(entity.getFixedAssetAmount());
        co.setArchiveRoomArea(entity.getArchiveRoomArea());
        co.setFulltimeEvaluatorCount(entity.getFulltimeEvaluatorCount());
        co.setRegisteredEngineerCount(entity.getRegisteredEngineerCount());
        co.setWorkplaceArea(entity.getWorkplaceArea());
        co.setUnitIntro(entity.getUnitIntro());
        co.setAttachmentUrl(entity.getAttachmentUrl());
        co.setFilingStatusCode(entity.getFilingStatusCode());
        co.setFilingStatusName(entity.getFilingStatusName());
        co.setTenantId(entity.getTenantId());
        return co;
    }
}
