package com.sino.mci.inbound.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.channel.entity.Conversation;
import com.sino.mci.channel.entity.ConversationTagBinding;
import com.sino.mci.channel.repository.ConversationMapper;
import com.sino.mci.channel.repository.ConversationTagBindingMapper;
import com.sino.mci.crm.entity.Person;
import com.sino.mci.crm.entity.PersonTagBinding;
import com.sino.mci.crm.entity.Tag;
import com.sino.mci.crm.repository.PersonMapper;
import com.sino.mci.crm.repository.PersonTagBindingMapper;
import com.sino.mci.crm.repository.TagMapper;
import com.sino.mci.inbound.entity.MessageEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * 入站消息上下文匹配服务。
 *
 * <p>根据消息事件关联的会话与联系人，解析业务上下文（合同、机构、服务商、标签等），
 * 供后续入站流程编排（意图识别、webhook 推送）使用。</p>
 */
@Service
@RequiredArgsConstructor
public class InboundContextService {

    private final ConversationMapper conversationMapper;
    private final PersonMapper personMapper;
    private final ConversationTagBindingMapper conversationTagBindingMapper;
    private final PersonTagBindingMapper personTagBindingMapper;
    private final TagMapper tagMapper;

    /**
     * 根据消息事件解析业务上下文。
     *
     * <p>解析规则：</p>
     * <ul>
     *   <li>会话维度：合同、服务商、机构、业务渠道编码、会话标签、群主账号；</li>
     *   <li>联系人维度：人员类型、机构、服务商、人员标签；</li>
     *   <li>事件维度：渠道类型、收发账号。</li>
     * </ul>
     * <p>当会话与联系人的机构/服务商不一致时，优先保留会话维度的值。</p>
     *
     * @param event 消息事件
     * @return 业务上下文键值对
     */
    public Map<String, Object> resolveContext(MessageEvent event) {
        Map<String, Object> context = new LinkedHashMap<>();

        resolveConversationContext(event, context);
        resolvePersonContext(event, context);

        if (event.getChannelType() != null) {
            context.put("channel_type", event.getChannelType());
        }
        if (event.getAccountId() != null) {
            context.put("account_id", event.getAccountId());
        }

        return context;
    }

    private void resolveConversationContext(MessageEvent event, Map<String, Object> context) {
        Long conversationId = event.getConversationId();
        if (conversationId == null) {
            return;
        }

        Conversation conversation = conversationMapper.selectById(conversationId);
        if (conversation == null) {
            return;
        }

        putIfNotNull(context, "contract_id", conversation.getContractId());
        putIfNotNull(context, "supplier_id", conversation.getSupplierId());
        putIfNotNull(context, "institution_id", conversation.getInstitutionId());
        putIfNotNull(context, "channel_code", conversation.getChannelCode());

        List<String> tagPaths = conversationTagBindingMapper.selectList(
                        new LambdaQueryWrapper<ConversationTagBinding>()
                                .eq(ConversationTagBinding::getConversationId, conversationId))
                .stream()
                .map(ConversationTagBinding::getTagId)
                .map(tagMapper::selectById)
                .filter(Objects::nonNull)
                .map(Tag::getTagPath)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (!tagPaths.isEmpty()) {
            context.put("conversation_tags", tagPaths);
        }

        putIfNotNull(context, "owner_account_id", conversation.getOwnerAccountId());
    }

    private void resolvePersonContext(MessageEvent event, Map<String, Object> context) {
        Long personId = event.getPersonId();
        if (personId == null) {
            return;
        }

        Person person = personMapper.selectById(personId);
        if (person == null) {
            return;
        }

        putIfNotNull(context, "person_type", person.getPersonType());
        putIfAbsentAndNotNull(context, "institution_id", person.getInstitutionId());
        putIfAbsentAndNotNull(context, "supplier_id", person.getSupplierId());

        List<String> tagPaths = personTagBindingMapper.selectList(
                        new LambdaQueryWrapper<PersonTagBinding>()
                                .eq(PersonTagBinding::getPersonId, personId))
                .stream()
                .map(PersonTagBinding::getTagId)
                .map(tagMapper::selectById)
                .filter(Objects::nonNull)
                .map(Tag::getTagPath)
                .filter(Objects::nonNull)
                .distinct()
                .collect(Collectors.toList());
        if (!tagPaths.isEmpty()) {
            context.put("person_tags", tagPaths);
        }
    }

    private void putIfNotNull(Map<String, Object> context, String key, Object value) {
        if (value != null) {
            context.put(key, value);
        }
    }

    private void putIfAbsentAndNotNull(Map<String, Object> context, String key, Object value) {
        if (value != null) {
            context.computeIfAbsent(key, k -> value);
        }
    }
}
