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

import cn.axzo.apollo.core.web.Result;
import cn.axzo.apollo.workspace.api.workspace.OrganizationalNodePractitionerWideApi;
import cn.axzo.apollo.workspace.api.workspace.WorkspaceApi;
import cn.axzo.apollo.workspace.api.workspace.req.OrganizationalNodePractitionerWideReqParams;
import cn.axzo.apollo.workspace.api.workspace.res.OrganizationalNodePractitionerWideDto;
import cn.axzo.apollo.workspace.api.workspace.res.SimpleWorkspaceRes;
import cn.axzo.apollo.workspace.common.enums.WorkspaceTypeEnum;
import cn.axzo.msg.center.domain.entity.Todo;
import cn.axzo.msg.center.domain.entity.TodoBusiness;
import cn.axzo.msg.center.message.domain.param.PendingMessagePushParam;
import cn.axzo.msg.center.service.dto.IdentityDTO;
import cn.axzo.msg.center.service.dto.PersonDTO;
import cn.axzo.msg.center.service.enums.BizCategoryEnum;
import cn.axzo.msg.center.service.enums.IdentityTypeEnum;
import cn.axzo.msg.center.service.enums.OrganizationTypeEnum;
import cn.axzo.msg.center.service.enums.PendingMessageStateEnum;
import cn.axzo.msg.center.service.enums.TodoType;
import cn.axzo.msg.center.service.enums.YesOrNo;
import cn.axzo.msg.center.utils.UUIDUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import javax.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * @author yanglin
 */
@Slf4j
@Component
@RequiredArgsConstructor
class TodoRecordBuilder {

    private static final Long DEFAULT_VALUE = 0L;

    private final OrganizationalNodePractitionerWideApi organizationalNodePractitionerWideApi;
    private final WorkspaceApi workspaceApi;

    TodoBusiness buildBusiness(PendingMessagePushParam request, TodoExt ext) {
        WorkspaceInfo workspaceInfo = getWorkspace(request.getPromoterWorkspaceId());
        PersonDTO promoter = request.getPromoter();
        TodoBusiness business = new TodoBusiness();
        business.setTemplateCode(request.getTemplateCode());
        business.setBizCode(request.getBizCode());
        business.setPromoterId(promoter == null ? 0L : promoter.identityIdOrDefault());
        business.setPromoterType(promoter == null
                ? IdentityTypeEnum.NOT_SUPPORT
                : promoter.identityTypeOrDefault());
        business.setPromoterPersonId(promoter == null ? 0 : promoter.personIdOrDefault());
        business.setPromoterName(promoter == null ? "" : promoter.getName());
        business.setOuId(determineOuId(request.getPromoterOuId(),
                workspaceInfo.workspace, promoter == null ? null : promoter.getIdentity()));
        business.setOrgId(request.getPromoterWorkspaceId());
        business.setOrgName(workspaceInfo.determineOrgName());
        business.setOrgType(workspaceInfo.determineOrgType(request.getPromoterOrgType()));
        business.setTitle(ext.parseTitle());
        business.setContent(ext.parseContent());
        business.setBizFlag(request.getBizFlag());
        business.setBizCategory(request.getBizCategory() == null
                ? BizCategoryEnum.OTHER
                : request.getBizCategory());
        business.setBizExtParam(ext.parseBizExtParams());
        business.setRouterParams(ext.parseRouterExtParams());
        business.setDeadline(request.getDeadline());
        business.setBizFinalState(null);
        // 不能存成null, 因为老的流程数据没有传这个按钮, 需要做查询兼容
        // 是否为老的流程数据, 主要判断这个字段是否为null
        business.setProposedButtons(request.determineProposedButtons());
        return business;
    }

    public List<Todo> buildTodos(
            PendingMessagePushParam request, String requestNo, TodoExt ext, TodoBusiness business) {
        WorkspaceInfo workspaceInfo = getWorkspace(request.getWorkspaceId());
        ArrayList<Todo> todos = new ArrayList<>();
        Set<Long> sentPersonIds = new HashSet<>();
        for (PersonDTO executor : request.getExecutor()) {
            if (sentPersonIds.contains(executor.getId()))
                continue;
            sentPersonIds.add(executor.getId());
            Todo todo = new Todo();
            todos.add(todo);
            todo.setTodoBusinessId(business.getId());
            todo.setSrcTemplateCode(business.getTemplateCode());
            todo.setTemplateCode(request.getTemplateCode());
            todo.setTitle(ext.parseTitle());
            todo.setContent(ext.parseContent());
            todo.setBizExtParam(ext.parseBizExtParams());
            todo.setRouterParams(ext.parseRouterExtParams());
            todo.setIdentityCode(UUIDUtil.uuidString());
            todo.setBizCode(request.getBizCode());
            todo.setSubBizCode(request.getSubBizCode());
            todo.setType(request.determineTodoType());
            todo.setOuId(determineOuId(request.getOuId(), workspaceInfo.workspace, executor.getIdentity()));
            todo.setOrgId(request.getWorkspaceId());
            todo.setOrgName(workspaceInfo.determineOrgName());
            todo.setOrgType(workspaceInfo.determineOrgType(request.getOrgType()));
            todo.setExecutorPersonId(executor.personIdOrDefault());
            todo.setExecutorName(executor.getName());
            todo.setRequestNo(requestNo);
            todo.setBizDesc(request.getBizDesc());
            todo.setIsOuIdMigrated(YesOrNo.NO);
            todo.setHideUntil(null);
            todo.setSupportBatchProcess(request.getSupportBatchProcess());
            todo.setState(request.determineTodoType() == TodoType.COPIED_TO_ME
                    ? PendingMessageStateEnum.CREATED
                    : PendingMessageStateEnum.HAS_BEEN_SENT);
            todo.setExecutorId(executor.identityIdOrDefault());
            todo.setExecutorType(executor.identityTypeOrDefault());
            // 不能存成null, 因为老的流程数据没有传这个按钮, 需要做查询兼容
            // 是否为老的流程数据, 主要判断这个字段是否为null
            todo.setProposedButtons(request.determineProposedButtons());
        }
        return todos;
    }

    @NotNull
    private WorkspaceInfo getWorkspace(Long workspaceId) {
        if (workspaceId == null || workspaceId <= 0)
            return new WorkspaceInfo(null);
        Result<SimpleWorkspaceRes> resp = workspaceApi.getOne(workspaceId);
        SimpleWorkspaceRes workspace = resp.getCode() == 200 ? resp.getData() : null;
        return new WorkspaceInfo(workspace);
    }

    private Long determineOuId(Long ouId, SimpleWorkspaceRes workspace, IdentityDTO identity) {
        if (ouId != null)
            return ouId;
        if (workspace != null && identity != null && identity.getType() == IdentityTypeEnum.PRACTITIONER) {
            OrganizationalNodePractitionerWideReqParams param = new OrganizationalNodePractitionerWideReqParams();
            param.setWorkspaceId(workspace.getId());
            param.setIdentityId(identity.getId());
            Result<OrganizationalNodePractitionerWideDto> result = organizationalNodePractitionerWideApi.getOne(param);
            if (null != result && null != result.getData() && null != result.getData().getOrganizationalUnitId())
                return result.getData().getOrganizationalUnitId();
        }
        HashMap<String, Object> params = new HashMap<>();
        params.put("ouId", null);
        params.put("workspace", workspace);
        params.put("identity", identity);
        log.warn("can't determine ouId, use default value: {}. params={}", DEFAULT_VALUE, params);
        return DEFAULT_VALUE;
    }

    private static class WorkspaceInfo {
        final SimpleWorkspaceRes workspace;

        WorkspaceInfo(SimpleWorkspaceRes workspace) {
            this.workspace = workspace;
        }

        OrganizationTypeEnum determineOrgType(OrganizationTypeEnum requestOrgType) {
            if (requestOrgType != null)
                return requestOrgType;
            if (workspace == null)
                return OrganizationTypeEnum.UNKNOWN;
            if (workspace.getType() == WorkspaceTypeEnum.GENERAL_ENT.getValue())
                return OrganizationTypeEnum.ENT;
            else if (workspace.getType() == WorkspaceTypeEnum.GENERAL_PROJECT.getValue())
                return OrganizationTypeEnum.PROJECT;
            else
                return OrganizationTypeEnum.UNKNOWN;
        }

        String determineOrgName() {
            return workspace == null ? "" : workspace.getName();
        }
    }

}