package com.sino.mci.channel.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.sino.mci.channel.dto.BindConversationAccountRequest;
import com.sino.mci.channel.dto.BindConversationTagsRequest;
import com.sino.mci.channel.dto.ConversationParticipantResponse;
import com.sino.mci.channel.dto.CreateConversationRequest;
import com.sino.mci.channel.dto.TagResponse;
import com.sino.mci.channel.dto.UpdateConversationRequest;
import com.sino.mci.channel.entity.ChannelAccount;
import com.sino.mci.channel.entity.ChannelAccountConversation;
import com.sino.mci.channel.entity.Conversation;
import com.sino.mci.channel.entity.ConversationParticipant;
import com.sino.mci.channel.entity.ConversationTagBinding;
import com.sino.mci.channel.repository.ChannelAccountConversationMapper;
import com.sino.mci.channel.repository.ChannelAccountMapper;
import com.sino.mci.channel.repository.ConversationMapper;
import com.sino.mci.channel.repository.ConversationParticipantMapper;
import com.sino.mci.channel.repository.ConversationTagBindingMapper;
import com.sino.mci.crm.entity.ChannelIdentity;
import com.sino.mci.crm.entity.Person;
import com.sino.mci.crm.entity.Tag;
import com.sino.mci.crm.repository.ChannelIdentityMapper;
import com.sino.mci.crm.repository.PersonMapper;
import com.sino.mci.crm.repository.TagMapper;
import com.sino.mci.exception.BusinessException;
import com.sino.mci.shared.util.JsonUtils;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ConversationService {

    private final ConversationMapper conversationMapper;
    private final ChannelAccountConversationMapper bindingMapper;
    private final ConversationTagBindingMapper conversationTagBindingMapper;
    private final TagMapper tagMapper;
    private final ChannelAccountMapper accountMapper;
    private final ConversationParticipantMapper participantMapper;
    private final ChannelIdentityMapper channelIdentityMapper;
    private final PersonMapper personMapper;

    public List<Conversation> listConversations(String tenantCode) {
        return conversationMapper.selectList(
                new LambdaQueryWrapper<Conversation>()
                        .eq(Conversation::getTenantCode, tenantCode)
                        .eq(Conversation::getStatus, 1)
                        .orderByDesc(Conversation::getId));
    }

    @Transactional
    public Conversation createConversation(String tenantCode, CreateConversationRequest request) {
        Conversation existing = conversationMapper.selectOne(
                new LambdaQueryWrapper<Conversation>()
                        .eq(Conversation::getTenantCode, tenantCode)
                        .eq(Conversation::getChannelType, request.getChannelType())
                        .eq(Conversation::getChannelConversationId, request.getChannelConversationId()));
        if (existing != null) {
            throw new BusinessException("会话已存在: " + request.getChannelConversationId());
        }

        Conversation conversation = new Conversation();
        conversation.setTenantCode(tenantCode);
        conversation.setConversationType(request.getConversationType());
        conversation.setChannelType(request.getChannelType());
        conversation.setChannelConversationId(request.getChannelConversationId());
        conversation.setOwnerAccountId(request.getOwnerAccountId());
        conversation.setSubjectId(request.getSubjectId());
        conversation.setConversationName(request.getConversationName());
        conversation.setCreateSource(request.getCreateSource() != null ? request.getCreateSource() : 2);
        conversation.setReceiveConfig(JsonUtils.toJson(request.getReceiveConfig()));
        conversation.setStatus(1);
        conversationMapper.insert(conversation);
        return conversation;
    }

    public Conversation getConversation(String tenantCode, Long conversationId) {
        Conversation conversation = conversationMapper.selectById(conversationId);
        if (conversation == null || !tenantCode.equals(conversation.getTenantCode())) {
            throw new BusinessException("会话不存在");
        }
        return conversation;
    }

    @Transactional
    public Conversation updateConversation(String tenantCode, Long conversationId, UpdateConversationRequest request) {
        Conversation conversation = getConversation(tenantCode, conversationId);
        conversation.setConversationName(request.getConversationName());
        if (request.getOwnerAccountId() != null) {
            conversation.setOwnerAccountId(request.getOwnerAccountId());
        }
        if (request.getSubjectId() != null) {
            conversation.setSubjectId(request.getSubjectId());
        }
        if (request.getStatus() != null) {
            conversation.setStatus(request.getStatus());
        }
        if (request.getReceiveConfig() != null) {
            conversation.setReceiveConfig(JsonUtils.toJson(request.getReceiveConfig()));
        }
        conversationMapper.updateById(conversation);
        return conversation;
    }

    @Transactional
    public void deleteConversation(String tenantCode, Long conversationId) {
        getConversation(tenantCode, conversationId);
        conversationMapper.deleteById(conversationId);
    }

    @Transactional
    public void bindAccount(String tenantCode, Long conversationId, BindConversationAccountRequest request) {
        Conversation conversation = getConversation(tenantCode, conversationId);

        ChannelAccount account = accountMapper.selectById(request.getAccountId());
        if (account == null || !tenantCode.equals(account.getTenantCode())) {
            throw new BusinessException("账号不存在或不在当前租户");
        }
        if (!conversation.getChannelType().equalsIgnoreCase(account.getChannelType())) {
            throw new BusinessException("账号渠道类型与会话不一致");
        }

        bindingMapper.delete(
                new LambdaQueryWrapper<ChannelAccountConversation>()
                        .eq(ChannelAccountConversation::getAccountId, request.getAccountId())
                        .eq(ChannelAccountConversation::getConversationId, conversationId)
                        .eq(ChannelAccountConversation::getBindingType, request.getBindingType()));

        ChannelAccountConversation binding = new ChannelAccountConversation();
        binding.setAccountId(request.getAccountId());
        binding.setConversationId(conversationId);
        binding.setBindingType(request.getBindingType());
        binding.setSortOrder(request.getSortOrder() != null ? request.getSortOrder() : 0);
        binding.setStatus(1);
        bindingMapper.insert(binding);
    }

    @Transactional
    public void bindTags(String tenantCode, Long conversationId, BindConversationTagsRequest request) {
        getConversation(tenantCode, conversationId);
        for (Long tagId : request.getTagIds()) {
            Tag tag = tagMapper.selectById(tagId);
            if (tag == null || !tenantCode.equals(tag.getTenantCode())) {
                throw new BusinessException("标签不存在: " + tagId);
            }
        }

        conversationTagBindingMapper.delete(
                new LambdaQueryWrapper<ConversationTagBinding>()
                        .eq(ConversationTagBinding::getConversationId, conversationId));

        if (!CollectionUtils.isEmpty(request.getTagIds())) {
            List<ConversationTagBinding> bindings = request.getTagIds().stream()
                    .distinct()
                    .map(tagId -> {
                        ConversationTagBinding binding = new ConversationTagBinding();
                        binding.setTenantCode(tenantCode);
                        binding.setTagId(tagId);
                        binding.setConversationId(conversationId);
                        return binding;
                    })
                    .collect(Collectors.toList());
            for (ConversationTagBinding binding : bindings) {
                conversationTagBindingMapper.insert(binding);
            }
        }
    }

    public List<ConversationParticipantResponse> listParticipants(String tenantCode, Long conversationId) {
        getConversation(tenantCode, conversationId);

        List<ConversationParticipant> participants = participantMapper.selectList(
                new LambdaQueryWrapper<ConversationParticipant>()
                        .eq(ConversationParticipant::getConversationId, conversationId)
                        .orderByAsc(ConversationParticipant::getId));

        if (participants.isEmpty()) {
            return Collections.emptyList();
        }

        Set<Long> channelIdentityIds = participants.stream()
                .map(ConversationParticipant::getChannelIdentityId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());
        Set<Long> personIds = participants.stream()
                .map(ConversationParticipant::getPersonId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        Map<Long, ChannelIdentity> identityMap = channelIdentityIds.isEmpty() ? Collections.emptyMap()
                : channelIdentityMapper.selectBatchIds(channelIdentityIds).stream()
                        .collect(Collectors.toMap(ChannelIdentity::getId, Function.identity()));
        Map<Long, Person> personMap = personIds.isEmpty() ? Collections.emptyMap()
                : personMapper.selectBatchIds(personIds).stream()
                        .collect(Collectors.toMap(Person::getId, Function.identity()));

        return participants.stream().map(participant -> {
            ConversationParticipantResponse response = new ConversationParticipantResponse();
            response.setId(participant.getId());
            response.setParticipantType(participant.getParticipantType());
            response.setJoinTime(participant.getJoinTime());
            response.setChannelIdentityId(participant.getChannelIdentityId());

            ChannelIdentity identity = participant.getChannelIdentityId() == null
                    ? null : identityMap.get(participant.getChannelIdentityId());
            response.setChannelUserId(identity == null ? null : identity.getChannelUserId());
            response.setChannelUserName(identity == null ? null : identity.getChannelUserName());

            Person person = participant.getPersonId() == null
                    ? null : personMap.get(participant.getPersonId());
            response.setPersonId(participant.getPersonId());
            response.setPersonName(person == null ? null : person.getPersonName());
            response.setPersonType(person == null ? null : person.getPersonType());

            return response;
        }).collect(Collectors.toList());
    }

    public List<TagResponse> listTags(String tenantCode, Long conversationId) {
        getConversation(tenantCode, conversationId);

        List<ConversationTagBinding> bindings = conversationTagBindingMapper.selectList(
                new LambdaQueryWrapper<ConversationTagBinding>()
                        .eq(ConversationTagBinding::getConversationId, conversationId)
                        .orderByAsc(ConversationTagBinding::getId));

        if (bindings.isEmpty()) {
            return Collections.emptyList();
        }

        Set<Long> tagIds = bindings.stream()
                .map(ConversationTagBinding::getTagId)
                .filter(Objects::nonNull)
                .collect(Collectors.toSet());

        Map<Long, Tag> tagMap = tagMapper.selectBatchIds(tagIds).stream()
                .collect(Collectors.toMap(Tag::getId, Function.identity()));

        return bindings.stream()
                .map(binding -> {
                    Tag tag = tagMap.get(binding.getTagId());
                    if (tag == null) {
                        return null;
                    }
                    TagResponse response = new TagResponse();
                    response.setId(tag.getId());
                    response.setTenantCode(tag.getTenantCode());
                    response.setParentId(tag.getParentId());
                    response.setTagPath(tag.getTagPath());
                    response.setLevel(tag.getLevel());
                    response.setTagName(tag.getTagName());
                    response.setTagSource(tag.getTagSource());
                    response.setStatus(tag.getStatus());
                    return response;
                })
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
    }
}
