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.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";

    public static String CHANGE_REASON = "changeReason";

    public static String CHANGE_ATTACHMENT = "changeAttachment";

    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)) {
            return;
        }

        // 预处理
        preSave(bizId, applyNo, model, changeData);
        JSONObject originalData = prepareOriginalData(changeData, applyNo);

        // 核心处理
        HandleResult handleResult = processDataChanges(bizId, model, changeData);

        // 后处理
        List<FieldChangeMeta> postColumnChanges = postSave(bizId, applyNo, model, changeData, Optional.ofNullable(handleResult.getFieldChangeMetas()).orElseGet(ArrayList::new), handleResult.getPipelineChangeItemMap());

        // 合并变更信息
        List<FieldChangeMeta> allChangeColumns = combineChanges(handleResult, postColumnChanges);

        // 发送数据变更消息
        publish2OtherBiz(allChangeColumns, applyNo, originalData, selectedOrgInfo);
    }

    private JSONObject prepareOriginalData(Map<String, Object> changeData, String applyNo) {
        JSONObject originalData = new JSONObject();
        BeanUtil.copyProperties(changeData, originalData);

        // 移除临时字段
        changeData.remove(CHANGE_REASON);
        changeData.remove(CHANGE_ATTACHMENT);

        // 添加处理标记
        changeData.put(IS_REQUIRES_TEMPORARY_SAVE, requiresTemporarySave(applyNo));
        changeData.put(TEMPORARY_PIPELINES_DATA, getsTemporaryData(applyNo));

        return originalData;
    }

    private HandleResult processDataChanges(String bizId,
                                            ModelType model,
                                            Map<String, Object> changeData) {
        IEquipChangeDataProcessStrategy processor = EquipDataProcessStrategyContext.getStrategy(model);
        return processor.handle(changeData, bizId);
    }


    private List<FieldChangeMeta> combineChanges(HandleResult handleResult,
                                                 List<FieldChangeMeta> bizChanges) {
        List<FieldChangeMeta> allChanges = Optional.ofNullable(handleResult.getFieldChangeMetas()).orElseGet(ArrayList::new);
        allChanges.addAll(bizChanges);
        return allChanges;
    }


    /**
     * 管道专用-是否需要暂存数据-暂存保存数据到json
     *
     * @param applyNo 单据号
     * @return true-存json;false-实时存库
     */
    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(CHANGE_REASON));
        bizRelationDataDto.setChangeAttachment(oData.get(CHANGE_ATTACHMENT) != null ? JSONObject.toJSONString(oData.get(CHANGE_ATTACHMENT)) : 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);
    }

}
