package com.yeejoin.amos.boot.module.jg.biz.edit.process.biz;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yeejoin.amos.boot.biz.common.bo.ReginParams;
import com.yeejoin.amos.boot.module.common.biz.event.CommonPublisher;
import com.yeejoin.amos.boot.module.jg.api.dto.BizRelationDataDto;
import com.yeejoin.amos.boot.module.jg.api.dto.FieldChangeMeta;
import com.yeejoin.amos.boot.module.jg.api.dto.PipelineChangeItemDto;
import com.yeejoin.amos.boot.module.jg.biz.context.EquipDataProcessStrategyContext;
import com.yeejoin.amos.boot.module.jg.biz.controller.BizDataChangeController;
import com.yeejoin.amos.boot.module.jg.biz.edit.event.BaseBizDataChangeEvent;
import com.yeejoin.amos.boot.module.jg.biz.edit.event.EmptyDataChangeEvent;
import com.yeejoin.amos.boot.module.jg.biz.edit.process.biz.strategy.IBizDataChangeHandleStrategy;
import com.yeejoin.amos.boot.module.jg.biz.edit.process.equip.CommonEquipDataProcessService;
import com.yeejoin.amos.boot.module.jg.biz.edit.process.equip.strategy.HandleResult;
import com.yeejoin.amos.boot.module.jg.biz.edit.process.equip.strategy.IEquipChangeDataProcessStrategy;
import com.yeejoin.amos.boot.module.jg.biz.service.impl.JgBizChangeLogServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEvent;
import org.typroject.tyboot.core.foundation.context.RequestContext;
import org.typroject.tyboot.core.restful.exception.instance.BadRequest;

import java.util.*;
import java.util.stream.Collectors;

@Slf4j
public abstract class DefaultBizDataChangeHandler<U extends BaseBizDataChangeEvent> implements IBizDataChangeHandleStrategy {

    public static String IS_REQUIRES_TEMPORARY_SAVE = "isRequiresTemporarySave";

    public static String TEMPORARY_PIPELINES_DATA = "temporaryPipelinesData";

    private final CommonPublisher eventPublisher;

    private final ApplicationContext applicationContext;

    protected DefaultBizDataChangeHandler(CommonPublisher eventPublisher, ApplicationContext applicationContext) {
        this.eventPublisher = eventPublisher;
        this.applicationContext = applicationContext;
    }


    @Override
    public final void doSave(String bizId, String applyNo, ModelType model, Map<String, Object> changeData, ReginParams selectedOrgInfo) {
        if (beforeCheck(bizId, applyNo, model, changeData)) {
            preSave(bizId, applyNo, model, changeData);
            JSONObject oData = new JSONObject();
            BeanUtil.copyProperties(changeData, oData);
            // 删除原始提交的变更说明及附件放置后续存放到json
            changeData.remove("changeReason");
            changeData.remove("changeAttachment");
            changeData.put(IS_REQUIRES_TEMPORARY_SAVE, requiresTemporarySave(applyNo));
            changeData.put(TEMPORARY_PIPELINES_DATA, getsTemporaryData(applyNo));
            IEquipChangeDataProcessStrategy dataProcessor = EquipDataProcessStrategyContext.getStrategy(model);
            HandleResult handleResult = dataProcessor.handle(changeData, bizId);
            List<FieldChangeMeta> allChangeColumns = handleResult.getFieldChangeMetas() == null ? new ArrayList<>() : handleResult.getFieldChangeMetas();
            List<FieldChangeMeta> bizEditColumns = postSave(bizId, applyNo, model, changeData, allChangeColumns, handleResult.getPipelineChangeItemMap());
            allChangeColumns.addAll(bizEditColumns);
            // 发送数据变更消息
            publish2OtherBiz(allChangeColumns, applyNo, oData, selectedOrgInfo);
        }
    }

    /**
     * 是否需要暂存数据-暂存保存数据到json--管道专用
     *
     * @param applyNo 单据号
     * @return yes-存json;no-实时存库
     */
    protected Boolean requiresTemporarySave(String applyNo) {
        return !bizIsFinished(applyNo);
    }

    /**
     * 各业务暂存的管道数据--管道专用
     *
     * @param applyNo 申请单号
     * @return 管道信息
     */
    protected List<PipelineChangeItemDto> getsTemporaryData(String applyNo) {
        return new ArrayList<>();
    }

    public abstract void preSave(String bizId, String applyNo, ModelType model, Map<String, Object> changeData);

    private void publish2OtherBiz(List<FieldChangeMeta> allChangeColumns, String applyNo, JSONObject oData, ReginParams selectedOrgInfo) {
        // 重复行数据不记录日志（如行政区划编码、行政区划名称，只记录一个字段即可）
        List<FieldChangeMeta> noPeatChangeFields = allChangeColumns.stream().filter(f -> !f.getIsRepeatColumn()).collect(Collectors.toList());
        BizRelationDataDto bizRelationDataDto = new BizRelationDataDto();
        bizRelationDataDto.setBizId(applyNo);
        bizRelationDataDto.setBizType(canHandleBizType());
        bizRelationDataDto.setRecords(getEqs(applyNo));
        bizRelationDataDto.setChangeReason(oData.getString("changeReason"));
        bizRelationDataDto.setChangeAttachment(oData.get("changeAttachment") != null ? JSONObject.toJSONString(oData.get("changeAttachment")) : null);
        bizRelationDataDto.setProjectContraptionIds(getProjectContraptionIds(applyNo));
        bizRelationDataDto.setRecUserName(selectedOrgInfo.getUserModel().getRealName());
        bizRelationDataDto.setUnitCode(selectedOrgInfo.getCompany().getCompanyCode());
        bizRelationDataDto.setUnitName(selectedOrgInfo.getCompany().getCompanyName());
        bizRelationDataDto.setBizIsFinished(bizIsFinished(applyNo));
        if (!noPeatChangeFields.isEmpty()) {
            eventPublisher.publish(new BaseBizDataChangeEvent(this, bizRelationDataDto, noPeatChangeFields, RequestContext.cloneRequestContext()));
        } else {
            eventPublisher.publish(new EmptyDataChangeEvent(this, bizRelationDataDto, RequestContext.cloneRequestContext()));
        }
    }

    @Override
    public IPage<?> getDetail(String applyNo, BizDataChangeController.DetailType type, JSONObject searchParams) {
        CommonEquipDataProcessService service = applicationContext.getBean(CommonEquipDataProcessService.class);
        switch (type) {
            case equip:
                return service.getEquListByRecords(getEqs(applyNo), searchParams);
            case project:
                return service.getProjectContraptionsBySeqs(getProjectContraptionIds(applyNo), searchParams);
            default:
                throw new BadRequest("unknown type for getDetail");
        }
    }

    @Override
    public Map<String, Object> getSubDetail(String applyNo, String bizId, BizDataChangeController.DetailType type) {
        CommonEquipDataProcessService service = applicationContext.getBean(CommonEquipDataProcessService.class);
        switch (type) {
            case equip:
                return service.getEquipDetailByRecord(bizId);
            case project:
                return service.getProjectDetailBySeq(bizId, getEqs(applyNo), requiresTemporarySave(applyNo), getsTemporaryData(applyNo));
            default:
                throw new BadRequest("unknown type for getSubDetail");
        }
    }

    @Override
    public IPage<?> getChangeLogs(String bizId, int current, int size) {
        Set<String> changeIds = new HashSet<>();
        changeIds.addAll(getEqs(bizId));
        changeIds.addAll(getProjectContraptionIds(bizId));
        JgBizChangeLogServiceImpl service = applicationContext.getBean(JgBizChangeLogServiceImpl.class);
        return service.queryPageListByChangeIds(changeIds, current, size);
    }
}
