package cn.axzo.msg.center.message.service.todo;

import cn.axzo.basics.common.BeanMapper;
import cn.axzo.msg.center.dal.SampleTodos;
import cn.axzo.msg.center.dal.TodoBusinessDao;
import cn.axzo.msg.center.dal.TodoBusinesses;
import cn.axzo.msg.center.dal.TodoDao;
import cn.axzo.msg.center.domain.entity.PendingRecordAdapter;
import cn.axzo.msg.center.domain.entity.Todo;
import cn.axzo.msg.center.domain.entity.TodoBusiness;
import cn.axzo.msg.center.inside.notices.config.PendingMessageBizConfig;
import cn.axzo.msg.center.message.domain.dto.TemplateModelV3;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Parser;
import cn.axzo.msg.center.message.service.impl.v3.ModelV3Service;
import cn.axzo.msg.center.message.service.todo.manage.TodoExt;
import cn.axzo.msg.center.service.domain.UrlConfig;
import cn.axzo.msg.center.service.dto.IdentityDTO;
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.pending.ClientRequest;
import cn.axzo.msg.center.service.pending.ModelVersion;
import cn.axzo.msg.center.service.pending.response.PendingMessageResponse;
import cn.axzo.msg.center.service.pending.response.PendingMessageSimpleDTO;
import cn.axzo.msg.center.service.pending.response.v3.ParsedModel3Visitor;
import cn.axzo.msg.center.service.pending.response.v3.ParsedModelV3Walker;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedCardUrl;
import cn.axzo.msg.center.service.pending.response.v3.model.ParsedModelV3;
import cn.axzo.msg.center.service.template.response.MessageDetailStyle;
import cn.axzo.msg.center.utils.JSONObjectUtil;
import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.ImmutableMap;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Component;

import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static java.util.function.UnaryOperator.identity;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toMap;

/**
 * 尽量使用已有的逻辑
 *
 * @author yanglin
 */
@Slf4j
@Component
@RequiredArgsConstructor
class TodoRespBuilder {

    private final TodoBusinessDao todoBusinessDao;
    private final TodoDao todoDao;
    private final ModelV3Service modelV3Service;
    private final PendingMessageBizConfig cfg;
    private final ModelV3Parser modelV3Parser;

    // batch convert to old model to reuse the code and save some time

    PendingMessageResponse toResponse(PendingRecordAdapter adapter,
                                      ClientRequest request) {
        List<PendingMessageResponse> responses = toResponse(
                Collections.singletonList(adapter), request);
        return responses.get(0);
    }

    List<PendingMessageResponse> toResponse(List<PendingRecordAdapter> adapters,
                                            ClientRequest request) {
        List<String> templateCodes = adapters.stream()
                .map(PendingRecordAdapter::getTemplateCode)
                .filter(Objects::nonNull)
                .distinct()
                .collect(toList());
        // 重新查询模版的收益大于成本
        List<TemplateModelV3> templateModels = modelV3Service.getByCodes(templateCodes);
        return adapters.stream()
                .map(adapter -> toResponse(adapter, templateModels, request))
                .collect(toList());
    }

    // convert to old model to reuse the code and save some time

    PendingMessageResponse toResponse(Todo todo,
                                      ClientRequest request) {
        TodoBusiness business = todoBusinessDao.getById(todo.getTodoBusinessId());
        return toResponse(business, todo, request);
    }

    PendingMessageResponse toResponse(TodoBusiness business,
                                      Todo todo,
                                      ClientRequest request) {
        TodoRecordAdapter adapter = TodoRecordAdapter.adaptTodo(business, todo);
        ArrayList<String> templateCodes = new ArrayList<>(2);
        if (business != null)
            templateCodes.add(business.getTemplateCode());
        if (todo != null)
            templateCodes.add(todo.getTemplateCode());
        List<TemplateModelV3> templateModels = modelV3Service.getByCodes(templateCodes);
        return toResponse(adapter, templateModels, request);
    }

    // !! build adapters

    PendingRecordAdapter buildBusinessAdapter(TodoBusiness business) {
        List<PendingRecordAdapter> adapters = buildBusinessAdapters(Collections.singletonList(business));
        return adapters.isEmpty() ? null : adapters.get(0);
    }

    List<PendingRecordAdapter> buildBusinessAdapters(List<TodoBusiness> businesses) {
        if (CollectionUtils.isEmpty(businesses))
            return Collections.emptyList();
        boolean emptyBizCodePresent = businesses.stream()
                .anyMatch(biz -> StringUtils.isBlank(biz.getBizCode()));
        boolean sampleTodoAbsent = businesses.stream()
                .anyMatch(biz -> StringUtils.isBlank(biz.getSampleTodoCode()));
        // 如果业务方没有传bizCode, 就取最近的待办, 防止模版变更
        SampleTodos samples = emptyBizCodePresent || sampleTodoAbsent
                ? todoDao.getLatestExecutableTodos(businesses)
                : todoDao.getSampleTodos(businesses);
        // 因为待办详情需要通过待办的 identityCode 来查询数据
        // 所以这里为了兼容老接口需要查询待办获取 identityCode
        ArrayList<PendingRecordAdapter> adapters = new ArrayList<>();
        for (TodoBusiness business : businesses) {
            Todo todo = samples.findSample(business.getId()).orElse(null);
            if (todo != null)
                adapters.add(TodoRecordAdapter.adaptBusiness(business, todo));
            else
                log.warn("Can't find sample todo for business. are todos deleted? businessId={}", business.getId());
        }
        return adapters;
    }

    List<PendingMessageSimpleDTO> buildTodosSimple(List<Todo> todos) {
        List<PendingRecordAdapter> adapters = buildTodoAdapters(todos);
        return adapters.stream()
                .map(MigrateUtils::convertAdapterToPending)
                .map(p -> BeanMapper.copyBean(p, PendingMessageSimpleDTO.class))
                .collect(toList());
    }

    List<PendingRecordAdapter> buildTodoAdapters(List<Todo> todos) {
        if (CollectionUtils.isEmpty(todos))
            return Collections.emptyList();
        TodoBusinesses businesses = todoBusinessDao.getBusinesses(todos);
        return todos.stream()
                .map(todo -> {
                    TodoBusiness business = businesses.findBusiness(todo).orElse(null);
                    return TodoRecordAdapter.adaptTodo(business, todo);
                })
                .collect(toList());
    }

    private PendingMessageResponse toResponse(PendingRecordAdapter adapter,
                                              List<TemplateModelV3> templateModels,
                                              @Nullable ClientRequest request) {
        PendingMessageResponse response = new PendingMessageResponse();

        // 发起人单位、项目信息
        response.setPromoterOuId(adapter.getPromoterOuId());
        response.setPromoterWorkspaceId(adapter.getPromoterOrgId());
        response.setPromoterWorkspaceName(adapter.getPromoterOrgName());
        response.setPromoterWorkspaceType(adapter.getPromoterOrgType());
        // 接收者单位、项目信息
        response.setExecutorOuId(adapter.getOuId());
        response.setExecutorWorkspaceId(adapter.getOrgId());
        response.setExecutorWorkspaceName(adapter.getOrgName());
        response.setExecutorWorkspaceType(adapter.getExecutorOrgType());
        // 兼容页面老逻辑
        response.setWorkspaceId(adapter.getPromoterOrgId());
        // 只有项目才设置发起人项目部名称, 单位的话不返回, 因为会导致待办详情中显示重复的单位信息
        if (adapter.getPromoterOrgType() == OrganizationTypeEnum.PROJECT)
            response.setWorkspaceName(adapter.getPromoterOrgName());
        else
            response.setWorkspaceName("");
        // 其它信息
        response.setTodoType(adapter.getTodoType());
        response.setIsRead(adapter.isRead());
        response.setProposedButtons(adapter.getCustomButtons());
        response.setBizExtParamObj(adapter.getBizExtParamObj());
        response.setRouterExtParamObj(adapter.getRouterParamObj());

        Map<String, TemplateModelV3> templateCode2Template = templateModels.stream()
                .collect(toMap(TemplateModelV3::getTemplateCode, identity()));
        TemplateModelV3 templateModel = templateCode2Template.get(adapter.getTemplateCode());
        if (templateModel != null)
            response.setTemplateCategory(templateModel.getTemplate().getMsgCategory());
        if (templateModel != null && templateModel.getTemplate().getDetailStyleCode() != null) {
            MessageDetailStyle detailStyle = cfg
                    .findMessageDetailType(templateModel.getTemplate().getDetailStyleCode())
                    .orElse(null);
            response.setDetailStyle(detailStyle);
        }
        // 解析并替换掉路由地址中的动态参数变量
        JSONObject routerParam = JSONObjectUtil.parseObject(adapter.getRouterParams());
        routerParam.put(TodoExt.CTX, ImmutableMap.of(
                "business", adapter.getTodoBusiness(),
                "todo", adapter.getTodo()));
        JSONObject authParam = new JSONObject();
        if (!routerParam.containsKey("ouId"))
            authParam.put("ouId", adapter.getOuId());
        if (!routerParam.containsKey("workspaceId"))
            authParam.put("workspaceId", adapter.getOrgId());
        authParam.putAll(routerParam);
        // 流程待办 - url无身份ID，手动拼接
        if (adapter.getBizCategory() == BizCategoryEnum.FLOW
                && (!authParam.containsKey("identityCode") || null == authParam.get("identityCode"))) {
            authParam.put("identityCode", adapter.getIdentityCode());
        }
        response.setIdentityCode(adapter.getIdentityCode());
        response.setTemplateCode(adapter.getTemplateCode());
        response.setTitle(adapter.getTitle());
        response.setContent(adapter.getContent());
        response.setState(adapter.getState());
        response.setBizCode(adapter.getBizCode());
        response.setSubBizCode(adapter.getSubBizCode());
        response.setBizDesc(adapter.getBizDesc());
        response.setBizFlag(adapter.getBizFlag());
        response.setOuId(adapter.getOuId());
        response.setBizCategory(adapter.getBizCategory());
        response.setBizFinalState(adapter.getBizFinalState());
        if (adapter.getBizFinalState() != null)
            response.setBizFinalStateIcon(response.getBizFinalState().getIcon());

        response.setIsCreatedToday(adapter.getCreateAt() != null
                && DateUtil.isSameDay(adapter.getCreateAt(), new Date()));
        response.setPromoterPersonId(adapter.getPromoterPersonId());
        response.setPromoterName(adapter.getPromoterName());
        response.setPromoterIdentity(IdentityDTO.builder()
                .id(adapter.getPromoterId()).type(adapter.getPromoterType()).build());
        response.setExecutorPersonId(adapter.getExecutorPersonId());
        response.setExecutorName(adapter.getExecutorName());
        response.setExecutorIdentity(IdentityDTO.builder()
                .id(adapter.getExecutorId()).type(adapter.getExecutorType()).build());
        response.setCreateTimestamp(adapter.getCreateAt() == null ? null : adapter.getCreateAt().getTime());
        response.setUpdateTimestamp(adapter.getUpdateAt() == null ? null : adapter.getUpdateAt().getTime());
        response.setDeadlineTimestamp(adapter.getDeadline() == null ? null : adapter.getDeadline().getTime());

        if (templateModel != null) {
            ParsedModelV3 parsedModel = modelV3Parser.parseModelForTodo(templateModel, adapter,
                    adapter.getBizExtParamObj(), authParam, routerParam, request);

            Runnable modelV3Builder = () -> {
                response.setModelV3(parsedModel);
                response.setDisplayOnCardKeyValues(parsedModel.determineDisplayOnCardKeyValues());
                response.setCardUrlOpenStrategy(templateModel.getTemplate().getCardUrlOpenStrategy());
                ParsedModelV3Walker.walkDown(parsedModel, new ParsedModel3Visitor() {
                    @Override
                    public void visitTemplateCardUrlConfig(UrlConfig urlConfig) {
                        ParsedCardUrl cardUrl = new ParsedCardUrl();
                        cardUrl.setUrl(modelV3Parser.parseSessionUrlInfo(urlConfig, request));
                        response.setCardUrl(cardUrl);
                    }
                });
            };

            if (request == null) {
                modelV3Builder.run();
            } else {
                ModelVersion version = ModelVersion.from(request);
                if (version.is(ModelVersion.V2) || version.is(ModelVersion.ALL))
                    ParsedModelV3Walker.walkDown(parsedModel,
                            new ModelV2PropsPopulator(cfg, response, request));
                if (version.is(ModelVersion.V3) || version.is(ModelVersion.ALL))
                    modelV3Builder.run();
            }
        }

        return response;
    }

}