package org.qinan.safetyeval.infrastructure.gatewayimpl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
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.QualFilingGateway;
import org.qinan.safetyeval.domain.query.QualFilingQuery;
import org.qinan.safetyeval.domain.query.PageResult;
import org.qinan.safetyeval.infrastructure.dataobject.*;
import org.qinan.safetyeval.infrastructure.mapper.*;
import org.qinan.safetyeval.infrastructure.support.InsertFieldDefaults;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 资质备案申请Gateway实现（Infrastructure层）
 *
 * @author safety-eval
 */
@Component
public class QualFilingGatewayImpl implements QualFilingGateway {

    private static final String SCENE_CHANGE = "change";

    private static final int APPLY_TYPE_APPLICATION = 1;
    private static final int APPLY_TYPE_FILED_MANAGE = 3;
    private static final int STATUS_FILED = 1;
    private static final int STATUS_CHANGE_REVIEWING = 4;

    @Resource
    private QualFilingMapper qualFilingMapper;
    @Resource
    private QualFilingCommitmentMapper qualFilingCommitmentMapper;
    @Resource
    private QualFilingEquipmentMapper qualFilingEquipmentMapper;
    @Resource
    private QualFilingMaterialMapper qualFilingMaterialMapper;
    @Resource
    private QualFilingPersonnelMapper qualFilingPersonnelMapper;
    @Resource
    private QualFilingPersonnelCertMapper qualFilingPersonnelCertMapper;
    @Resource
    private OrgPersonnelCertMapper orgPersonnelCertMapper;
    @Autowired
    private QualFilingReviewMapper qualFilingReviewMapper;

    @Override
    public QualFilingEntity save(QualFilingEntity entity) {
        QualFilingDO dataObject = toDO(entity);
        InsertFieldDefaults.apply(dataObject);
        qualFilingMapper.insert(dataObject);
        entity.setId(dataObject.getId());
        return entity;
    }

    @Override
    public QualFilingEntity get(Long id) {
        QualFilingDO dataObject = qualFilingMapper.selectById(id);
        return toEntity(dataObject);
    }
@Override
    public QualFilingEntity getByFilingNo(String filingNo) {
        LambdaQueryWrapper<QualFilingDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QualFilingDO::getFilingNo, filingNo);
        wrapper.eq(QualFilingDO::getDeleteEnum, "false");
        QualFilingDO dataObject = qualFilingMapper.selectOne(wrapper);
        return toEntity(dataObject);
    }

    @Override
    public QualFilingEntity modify(QualFilingEntity entity) {
        QualFilingDO dataObject = toDO(entity);
        dataObject.setId(entity.getId());
        InsertFieldDefaults.applyForUpdate(dataObject);
        qualFilingMapper.updateById(dataObject);
        return get(entity.getId());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void delete(Long id) {
        qualFilingMapper.deleteById(id);
        qualFilingCommitmentMapper.delete(new LambdaQueryWrapper<QualFilingCommitmentDO>().eq(QualFilingCommitmentDO::getFilingId, id));
        qualFilingEquipmentMapper.delete(new LambdaQueryWrapper<QualFilingEquipmentDO>().eq(QualFilingEquipmentDO::getFilingId, id));
        qualFilingMaterialMapper.delete(new LambdaQueryWrapper<QualFilingMaterialDO>().eq(QualFilingMaterialDO::getFilingId, id));
        qualFilingPersonnelMapper.delete(new LambdaQueryWrapper<QualFilingPersonnelDO>().eq(QualFilingPersonnelDO::getFilingId, id));
        qualFilingPersonnelCertMapper.delete(new LambdaQueryWrapper<QualFilingPersonnelCertDO>().eq(QualFilingPersonnelCertDO::getFilingId, id));
    }

    @Override
    public PageResult<QualFilingEntity> page(QualFilingQuery query) {
        LambdaQueryWrapper<QualFilingDO> wrapper = new LambdaQueryWrapper<>();
        wrapper.eq(QualFilingDO::getDeleteEnum, "false");
        if (query.getOrgId() != null) {
            wrapper.eq(QualFilingDO::getOrgId, query.getOrgId());
        }
        if (StringUtils.hasText(query.getFilingNo())) {
            wrapper.eq(QualFilingDO::getFilingNo, query.getFilingNo());
        }
        if (StringUtils.hasText(query.getFilingUnitName())) {
            wrapper.like(QualFilingDO::getFilingUnitName, query.getFilingUnitName());
        }
        if (StringUtils.hasText(query.getFilingTerritoryCode())) {
            wrapper.eq(QualFilingDO::getFilingTerritoryCode, query.getFilingTerritoryCode());
        }
        if (query.getSupervision()) {
            wrapper.ne(QualFilingDO::getFilingStatusCode, 5);
        }
        wrapper.orderByDesc(QualFilingDO::getCreateTime);

        Page<QualFilingDO> page = new Page<>(query.getPageNum(), query.getPageSize());
        IPage<QualFilingDO> result = qualFilingMapper.selectPage(page, wrapper);

        List<QualFilingEntity> entities = result.getRecords().stream()
                .map(this::toEntity)
                .collect(Collectors.toList());

        return PageResult.of(entities, result.getTotal(), result.getCurrent(), result.getSize());
    }

    /**
     * 聚合保存
     * @param entity 基础信息
     * @param commitmentEntity 备案承诺书
     * @param equipmentEntities 备件
     * @param materialEntities 备案材料
     * @param personnelEntities 备案人员
     * @param personnelCertEntities 备案人员证书
     */

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Long aggregationSaveOrEdit(QualFilingEntity entity, QualFilingCommitmentEntity commitmentEntity, List<QualFilingEquipmentEntity> equipmentEntities, List<QualFilingMaterialEntity> materialEntities, List<QualFilingPersonnelEntity> personnelEntities, List<QualFilingPersonnelCertEntity> personnelCertEntities) {
        //保存基础信息
        QualFilingDO dataObject = new QualFilingDO();
        BeanUtils.copyProperties(entity, dataObject);
        if (Objects.nonNull(dataObject.getId())) {
            // 编辑
            InsertFieldDefaults.applyForUpdate(dataObject);
            qualFilingMapper.updateById(dataObject);
        } else {
            InsertFieldDefaults.apply(dataObject);
            qualFilingMapper.insert(dataObject);
        }
        if (Objects.isNull(dataObject.getId())) {
            throw new BizException(ErrorCode.QUAL_FILING_STATUS_INVALID);
        }
        // 先删除对应数据,在新增
        qualFilingCommitmentMapper.delete(new LambdaQueryWrapper<QualFilingCommitmentDO>().eq(QualFilingCommitmentDO::getFilingId, dataObject.getId()));
        qualFilingEquipmentMapper.delete(new LambdaQueryWrapper<QualFilingEquipmentDO>().eq(QualFilingEquipmentDO::getFilingId, dataObject.getId()));
        qualFilingMaterialMapper.delete(new LambdaQueryWrapper<QualFilingMaterialDO>().eq(QualFilingMaterialDO::getFilingId, dataObject.getId()));
        qualFilingPersonnelMapper.delete(new LambdaQueryWrapper<QualFilingPersonnelDO>().eq(QualFilingPersonnelDO::getFilingId, dataObject.getId()));
        qualFilingPersonnelCertMapper.delete(new LambdaQueryWrapper<QualFilingPersonnelCertDO>().eq(QualFilingPersonnelCertDO::getFilingId, dataObject.getId()));

        //备案承诺书
        QualFilingCommitmentDO commitmentDataObject = new QualFilingCommitmentDO();
        BeanUtils.copyProperties(commitmentEntity, commitmentDataObject);
        InsertFieldDefaults.apply(commitmentDataObject);
        commitmentDataObject.setFilingId(dataObject.getId());
        qualFilingCommitmentMapper.insert(commitmentDataObject);
        //备案装备
        equipmentEntities.stream().map(item -> {
            QualFilingEquipmentDO equipmentDataObject = new QualFilingEquipmentDO();
            BeanUtils.copyProperties(item, equipmentDataObject);
            InsertFieldDefaults.apply(equipmentDataObject);
            equipmentDataObject.setFilingId(dataObject.getId());
            return equipmentDataObject;
        }).collect(Collectors.toList()).forEach(equipmentDataObject -> qualFilingEquipmentMapper.insert(equipmentDataObject));
        //备案材料
        materialEntities.stream().map(item -> {
            QualFilingMaterialDO materialDataObject = new QualFilingMaterialDO();
            BeanUtils.copyProperties(item, materialDataObject);
            InsertFieldDefaults.apply(materialDataObject);
            materialDataObject.setFilingId(dataObject.getId());
            return materialDataObject;
        }).collect(Collectors.toList()).forEach(materialDataObject -> qualFilingMaterialMapper.insert(materialDataObject));
        //备案人员
        personnelEntities.stream().map(item -> {
            QualFilingPersonnelDO personnelDataObject = new QualFilingPersonnelDO();
            BeanUtils.copyProperties(item, personnelDataObject);
            InsertFieldDefaults.apply(personnelDataObject);
            personnelDataObject.setFilingId(dataObject.getId());
            return personnelDataObject;
        }).collect(Collectors.toList()).forEach(personnelDataObject -> qualFilingPersonnelMapper.insert(personnelDataObject));
        //备案人员证书
        if (CollectionUtils.isEmpty(personnelCertEntities)) {
            List<Long> sourcePersonnelIds = personnelEntities.stream().map(QualFilingPersonnelEntity::getSourcePersonnelId).collect(Collectors.toList());
            if (!CollectionUtils.isEmpty(sourcePersonnelIds)) {
                List<OrgPersonnelCertDO> orgPersonnelCertDOS = orgPersonnelCertMapper.selectList(new LambdaQueryWrapper<OrgPersonnelCertDO>()
                        .in(OrgPersonnelCertDO::getPersonnelId, sourcePersonnelIds));
                if (!CollectionUtils.isEmpty(orgPersonnelCertDOS)) {
                    for (OrgPersonnelCertDO orgPersonnelCertDO : orgPersonnelCertDOS) {
                        QualFilingPersonnelCertDO qualFilingPersonnelCertDO = new QualFilingPersonnelCertDO();
                        BeanUtils.copyProperties(orgPersonnelCertDO, qualFilingPersonnelCertDO, "id");
                        InsertFieldDefaults.apply(qualFilingPersonnelCertDO);
                        qualFilingPersonnelCertDO.setId(null);
                        qualFilingPersonnelCertDO.setFilingId(dataObject.getId());
                        qualFilingPersonnelCertDO.setFilingPersonnelId(orgPersonnelCertDO.getPersonnelId());
                        qualFilingPersonnelCertDO.setSourceCertId(orgPersonnelCertDO.getId());
                        qualFilingPersonnelCertMapper.insert(qualFilingPersonnelCertDO);
                    }
                }
            }
        } else {
            personnelCertEntities.stream().map(item -> {
                QualFilingPersonnelCertDO personnelCertDataObject = new QualFilingPersonnelCertDO();
                BeanUtils.copyProperties(item, personnelCertDataObject, "id");
                InsertFieldDefaults.apply(personnelCertDataObject);
                personnelCertDataObject.setFilingId(dataObject.getId());
                return personnelCertDataObject;
            }).collect(Collectors.toList()).forEach(personnelCertDataObject -> qualFilingPersonnelCertMapper.insert(personnelCertDataObject));
        }
        return dataObject.getId();
    }

    /**
     * 备案审核
     * @param filingId 机构id
     * @param reviewResult 审核结果(1通过2待定3不通过)
     * @param reviewResultRemark 审核结果文字描述
     * @param filingExpertId 专家id
     * @param filingExpertName 专家名称
     * @param reviewOpinion 审核意见
     */
    @Override
    public void review(Long filingId, String reviewResult, String reviewResultRemark, Long filingExpertId, String filingExpertName, String reviewOpinion) {
        // 保存审核记录
        QualFilingReviewDO qualFilingReviewDO = new QualFilingReviewDO();
        qualFilingReviewDO.setFilingId(filingId);
        qualFilingReviewDO.setReviewResult(reviewResult);
        qualFilingReviewDO.setReviewResultRemark(reviewResultRemark);
        qualFilingReviewDO.setFilingExpertId(filingExpertId);
        qualFilingReviewDO.setFilingExpertName(filingExpertName);
        qualFilingReviewDO.setReviewOpinion(reviewOpinion);
        InsertFieldDefaults.apply(qualFilingReviewDO);
        qualFilingReviewMapper.insert(qualFilingReviewDO);
        // 更新备案状态
        QualFilingDO filing = new QualFilingDO();
        QualFilingStatusEnum qualFilingStatusEnum = QualFilingStatusEnum.ofCode(Integer.valueOf(reviewResult));
        filing.setFilingStatusCode(qualFilingStatusEnum.getCode());
        filing.setFilingStatusName(qualFilingStatusEnum.getName());
        filing.setApproveTime(LocalDateTime.now());
        filing.setId(filingId);
        filing.setFilingReviewId(qualFilingReviewDO.getId());
        InsertFieldDefaults.applyForUpdate(filing);
        qualFilingMapper.updateById(filing);
    }

    /**
     * 备案变更列表：优先展示已备案主记录(applyType=3)；
     * 若仅有资质备案申请(applyType=1)为已备案、尚无主记录时，回退展示申请单。
     */
    private void applyChangeListFilter(LambdaQueryWrapper<QualFilingDO> wrapper, QualFilingQuery query) {
        List<Integer> statuses = resolveStatusFilter(query, Arrays.asList(STATUS_FILED, STATUS_CHANGE_REVIEWING));
        wrapper.and(w -> {
            w.nested(n -> n
                    .eq(QualFilingDO::getApplyTypeCode, APPLY_TYPE_FILED_MANAGE)
                    .isNull(QualFilingDO::getOriginFilingId)
                    .in(QualFilingDO::getFilingStatusCode, statuses));
            if (statuses.contains(STATUS_FILED)) {
                w.or(n -> n
                        .eq(QualFilingDO::getApplyTypeCode, APPLY_TYPE_APPLICATION)
                        .eq(QualFilingDO::getFilingStatusCode, STATUS_FILED)
                        .apply("NOT EXISTS (SELECT 1 FROM qual_filing f2 WHERE f2.org_id = qual_filing.org_id"
                                + " AND f2.apply_type_code = " + APPLY_TYPE_FILED_MANAGE
                                + " AND f2.origin_filing_id IS NULL"
                                + " AND f2.delete_enum = 'false'"
                                + " AND f2.filing_status_code IN (" + STATUS_FILED + "," + STATUS_CHANGE_REVIEWING + "))"));
            }
        });
    }

    private List<Integer> resolveStatusFilter(QualFilingQuery query, List<Integer> defaults) {
        if (query.getFilingStatusCodes() != null && !query.getFilingStatusCodes().isEmpty()) {
            return query.getFilingStatusCodes();
        }
        if (query.getFilingStatusCode() != null) {
            return Collections.singletonList(query.getFilingStatusCode());
        }
        return new ArrayList<>(defaults);
    }

    private QualFilingDO toDO(QualFilingEntity entity) {
        QualFilingDO dataObject = new QualFilingDO();
        dataObject.setOrgId(entity.getOrgId());
        dataObject.setFilingTerritoryCode(entity.getFilingTerritoryCode());
        dataObject.setFilingTerritoryName(entity.getFilingTerritoryName());
        dataObject.setFilingUnitName(entity.getFilingUnitName());
        dataObject.setFilingUnitTypeCode(entity.getFilingUnitTypeCode());
        dataObject.setFilingUnitTypeName(entity.getFilingUnitTypeName());
        dataObject.setFilingNo(entity.getFilingNo());
        dataObject.setBusinessScope(entity.getBusinessScope());
        dataObject.setRegisterAddress(entity.getRegisterAddress());
        dataObject.setOfficeAddress(entity.getOfficeAddress());
        dataObject.setCreditCode(entity.getCreditCode());
        dataObject.setQualCertNo(entity.getQualCertNo());
        dataObject.setLegalPersonPhone(entity.getLegalPersonPhone());
        dataObject.setContactPhone(entity.getContactPhone());
        dataObject.setInfoDisclosureUrl(entity.getInfoDisclosureUrl());
        dataObject.setFixedAssetAmount(entity.getFixedAssetAmount());
        dataObject.setArchiveRoomArea(entity.getArchiveRoomArea());
        dataObject.setFulltimeEvaluatorCount(entity.getFulltimeEvaluatorCount());
        dataObject.setRegisteredEngineerCount(entity.getRegisteredEngineerCount());
        dataObject.setWorkplaceArea(entity.getWorkplaceArea());
        dataObject.setUnitIntro(entity.getUnitIntro());
        dataObject.setAttachmentUrl(entity.getAttachmentUrl());
        dataObject.setOriginFilingId(entity.getOriginFilingId());
        dataObject.setRejectReason(entity.getRejectReason());
        dataObject.setFilingReviewId(entity.getFilingReviewId());
        dataObject.setSubmitTime(entity.getSubmitTime());
        dataObject.setApproveTime(entity.getApproveTime());
        dataObject.setFilingStatusCode(entity.getFilingStatusCode());
        dataObject.setFilingStatusName(entity.getFilingStatusName());
        dataObject.setApplyTypeCode(entity.getApplyTypeCode());
        dataObject.setApplyTypeName(entity.getApplyTypeName());
        dataObject.setTenantId(entity.getTenantId());
        return dataObject;
    }

    private QualFilingEntity toEntity(QualFilingDO dataObject) {
        if (dataObject == null) {
            return null;
        }
        QualFilingEntity entity = new QualFilingEntity();
        entity.setId(dataObject.getId());
        entity.setOrgId(dataObject.getOrgId());
        entity.setFilingTerritoryCode(dataObject.getFilingTerritoryCode());
        entity.setFilingTerritoryName(dataObject.getFilingTerritoryName());
        entity.setFilingUnitName(dataObject.getFilingUnitName());
        entity.setFilingUnitTypeCode(dataObject.getFilingUnitTypeCode());
        entity.setFilingUnitTypeName(dataObject.getFilingUnitTypeName());
        entity.setFilingNo(dataObject.getFilingNo());
        entity.setBusinessScope(dataObject.getBusinessScope());
        entity.setRegisterAddress(dataObject.getRegisterAddress());
        entity.setOfficeAddress(dataObject.getOfficeAddress());
        entity.setCreditCode(dataObject.getCreditCode());
        entity.setQualCertNo(dataObject.getQualCertNo());
        entity.setLegalPersonPhone(dataObject.getLegalPersonPhone());
        entity.setContactPhone(dataObject.getContactPhone());
        entity.setInfoDisclosureUrl(dataObject.getInfoDisclosureUrl());
        entity.setFixedAssetAmount(dataObject.getFixedAssetAmount());
        entity.setArchiveRoomArea(dataObject.getArchiveRoomArea());
        entity.setFulltimeEvaluatorCount(dataObject.getFulltimeEvaluatorCount());
        entity.setRegisteredEngineerCount(dataObject.getRegisteredEngineerCount());
        entity.setWorkplaceArea(dataObject.getWorkplaceArea());
        entity.setUnitIntro(dataObject.getUnitIntro());
        entity.setAttachmentUrl(dataObject.getAttachmentUrl());
        entity.setOriginFilingId(dataObject.getOriginFilingId());
        entity.setRejectReason(dataObject.getRejectReason());
        entity.setFilingReviewId(dataObject.getFilingReviewId());
        entity.setSubmitTime(dataObject.getSubmitTime());
        entity.setApproveTime(dataObject.getApproveTime());
        entity.setFilingStatusCode(dataObject.getFilingStatusCode());
        entity.setFilingStatusName(dataObject.getFilingStatusName());
        entity.setApplyTypeCode(dataObject.getApplyTypeCode());
        entity.setApplyTypeName(dataObject.getApplyTypeName());
        entity.setTenantId(dataObject.getTenantId());
        return entity;
    }
}
