package com.sino.mci.channel.service;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
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.repository.ChannelAccountConversationMapper;
import com.sino.mci.channel.repository.ConversationMapper;
import com.sino.mci.channel.repository.ConversationParticipantMapper;
import com.sino.mci.channel.wecom.sync.WeComContact;
import com.sino.mci.channel.wecom.sync.WeComContactType;
import com.sino.mci.channel.wecom.sync.WeComConversation;
import com.sino.mci.channel.wecom.sync.WeComConversationMember;
import com.sino.mci.crm.entity.ChannelIdentity;
import com.sino.mci.crm.entity.Person;
import com.sino.mci.crm.repository.ChannelIdentityMapper;
import com.sino.mci.crm.repository.PersonMapper;
import com.sino.mci.shared.util.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;

@Slf4j
@Component
@RequiredArgsConstructor
public class SyncDataPersister {

    private static final String CHANNEL_WECOM = "wecom";
    private static final int BINDING_SEND_MASTER = 1;
    private static final int BINDING_RECEIVE_MASTER = 3;
    private static final int PERSON_TYPE_INTERNAL = 1;
    private static final int PERSON_TYPE_CUSTOMER = 3;
    private static final int CONVERSATION_TYPE_GROUP = 2;
    private static final int PARTICIPANT_TYPE_MEMBER = 1;
    private static final int PARTICIPANT_TYPE_OWNER = 2;

    private final PersonMapper personMapper;
    private final ChannelIdentityMapper channelIdentityMapper;
    private final ConversationMapper conversationMapper;
    private final ConversationParticipantMapper conversationParticipantMapper;
    private final ChannelAccountConversationMapper accountConversationMapper;

    @Transactional
    public void upsertContact(String tenantCode, Long subjectId, WeComContact contact) {
        ChannelIdentity identity = channelIdentityMapper.selectOne(
                new LambdaQueryWrapper<ChannelIdentity>()
                        .eq(ChannelIdentity::getTenantCode, tenantCode)
                        .eq(ChannelIdentity::getChannelType, CHANNEL_WECOM)
                        .eq(ChannelIdentity::getChannelUserId, contact.getUserId()));

        Person person;
        if (identity != null) {
            person = personMapper.selectById(identity.getPersonId());
            if (person == null) {
                log.warn("ChannelIdentity {} references missing person {}", identity.getId(), identity.getPersonId());
                return;
            }
            Long originalInstitutionId = person.getInstitutionId();
            Long originalSupplierId = person.getSupplierId();
            Integer originalPersonType = person.getPersonType();
            person.setPersonName(contact.getName());
            person.setPhone(contact.getMobile());
            person.setInstitutionId(originalInstitutionId);
            person.setSupplierId(originalSupplierId);
            person.setPersonType(originalPersonType);
            personMapper.updateById(person);

            identity.setChannelUserName(contact.getName());
            identity.setExtInfo(JsonUtils.toJson(contact));
            channelIdentityMapper.updateById(identity);
        } else {
            person = new Person();
            person.setTenantCode(tenantCode);
            person.setPersonCode(contact.getUserId());
            person.setPersonName(contact.getName());
            person.setPhone(contact.getMobile());
            person.setPersonType(resolvePersonType(contact));
            person.setStatus(1);
            personMapper.insert(person);

            ChannelIdentity newIdentity = new ChannelIdentity();
            newIdentity.setTenantCode(tenantCode);
            newIdentity.setPersonId(person.getId());
            newIdentity.setChannelType(CHANNEL_WECOM);
            newIdentity.setChannelUserId(contact.getUserId());
            newIdentity.setChannelUserName(contact.getName());
            newIdentity.setSubjectId(subjectId);
            newIdentity.setStatus(1);
            newIdentity.setExtInfo(JsonUtils.toJson(contact));
            channelIdentityMapper.insert(newIdentity);
        }
    }

    @Transactional
    public Conversation upsertConversation(ChannelAccount account, Long subjectId, WeComConversation conv) {
        Conversation conversation = conversationMapper.selectOne(
                new LambdaQueryWrapper<Conversation>()
                        .eq(Conversation::getTenantCode, account.getTenantCode())
                        .eq(Conversation::getChannelType, CHANNEL_WECOM)
                        .eq(Conversation::getChannelConversationId, conv.getChatId()));

        if (conversation != null) {
            Long originalInstitutionId = conversation.getInstitutionId();
            Long originalSupplierId = conversation.getSupplierId();
            Long originalContractId = conversation.getContractId();
            String originalChannelCode = conversation.getChannelCode();
            conversation.setConversationName(conv.getChatName());
            conversation.setOwnerAccountId(account.getId());
            conversation.setSubjectId(subjectId);
            conversation.setInstitutionId(originalInstitutionId);
            conversation.setSupplierId(originalSupplierId);
            conversation.setContractId(originalContractId);
            conversation.setChannelCode(originalChannelCode);
            conversationMapper.updateById(conversation);
            return conversation;
        }

        Conversation newConversation = new Conversation();
        newConversation.setTenantCode(account.getTenantCode());
        newConversation.setConversationType(CONVERSATION_TYPE_GROUP);
        newConversation.setChannelType(CHANNEL_WECOM);
        newConversation.setChannelConversationId(conv.getChatId());
        newConversation.setOwnerAccountId(account.getId());
        newConversation.setSubjectId(subjectId);
        newConversation.setConversationName(conv.getChatName());
        newConversation.setCreateSource(1);
        newConversation.setStatus(1);
        conversationMapper.insert(newConversation);
        return newConversation;
    }

    @Transactional
    public void refreshParticipants(String tenantCode, Long subjectId, Conversation conversation, WeComConversation conv) {
        conversationParticipantMapper.physicalDeleteByConversationId(conversation.getId());

        for (WeComConversationMember member : conv.getMemberList()) {
            log.debug("[SyncDataPersister] 群聊成员 userId={}, type={}", member.getUserId(), member.getType());
            ChannelIdentity identity = findOrCreateIdentity(tenantCode, subjectId, member);

            ConversationParticipant participant = new ConversationParticipant();
            participant.setConversationId(conversation.getId());
            participant.setPersonId(identity.getPersonId());
            participant.setChannelIdentityId(identity.getId());
            participant.setParticipantType(resolveParticipantType(member, conv.getOwnerUserId()));
            participant.setJoinTime(LocalDateTime.now());
            conversationParticipantMapper.insert(participant);
        }
    }

    @Transactional
    public void bindAccountAsDefault(Long accountId, Long conversationId) {
        ensureBinding(accountId, conversationId, BINDING_SEND_MASTER);
        ensureBinding(accountId, conversationId, BINDING_RECEIVE_MASTER);
    }

    private ChannelIdentity findOrCreateIdentity(String tenantCode, Long subjectId, WeComConversationMember member) {
        ChannelIdentity identity = channelIdentityMapper.selectOne(
                new LambdaQueryWrapper<ChannelIdentity>()
                        .eq(ChannelIdentity::getTenantCode, tenantCode)
                        .eq(ChannelIdentity::getChannelType, CHANNEL_WECOM)
                        .eq(ChannelIdentity::getChannelUserId, member.getUserId()));

        int personType = resolveMemberPersonType(member.getType());
        if (identity != null) {
            Person existPerson = personMapper.selectById(identity.getPersonId());
            if (existPerson != null && !existPerson.getPersonType().equals(personType)) {
                existPerson.setPersonType(personType);
                personMapper.updateById(existPerson);
            }
            return identity;
        }

        String displayName = resolveDisplayName(member.getName(), member.getUserId());
        Person person = new Person();
        person.setTenantCode(tenantCode);
        person.setPersonCode(member.getUserId());
        person.setPersonName(displayName);
        person.setPersonType(personType);
        person.setStatus(1);
        personMapper.insert(person);

        ChannelIdentity newIdentity = new ChannelIdentity();
        newIdentity.setTenantCode(tenantCode);
        newIdentity.setPersonId(person.getId());
        newIdentity.setChannelType(CHANNEL_WECOM);
        newIdentity.setChannelUserId(member.getUserId());
        newIdentity.setChannelUserName(displayName);
        newIdentity.setSubjectId(subjectId);
        newIdentity.setStatus(1);
        channelIdentityMapper.insert(newIdentity);
        return newIdentity;
    }

    private String resolveDisplayName(String name, String userId) {
        if (name != null && !name.isBlank()) {
            return name;
        }
        return userId != null && !userId.isBlank() ? userId : "未知";
    }

    private int resolveMemberPersonType(Integer memberType) {
        // 企微群聊成员类型：1=企业成员，2=外部联系人
        return memberType != null && memberType == 1 ? PERSON_TYPE_INTERNAL : PERSON_TYPE_CUSTOMER;
    }

    private void ensureBinding(Long accountId, Long conversationId, int bindingType) {
        ChannelAccountConversation existing = accountConversationMapper.selectOne(
                new LambdaQueryWrapper<ChannelAccountConversation>()
                        .eq(ChannelAccountConversation::getAccountId, accountId)
                        .eq(ChannelAccountConversation::getConversationId, conversationId)
                        .eq(ChannelAccountConversation::getBindingType, bindingType));
        if (existing != null) {
            return;
        }

        ChannelAccountConversation binding = new ChannelAccountConversation();
        binding.setAccountId(accountId);
        binding.setConversationId(conversationId);
        binding.setBindingType(bindingType);
        binding.setSortOrder(0);
        binding.setStatus(1);
        accountConversationMapper.insert(binding);
    }

    private int resolvePersonType(WeComContact contact) {
        return contact.getType() == WeComContactType.INTERNAL ? PERSON_TYPE_INTERNAL : PERSON_TYPE_CUSTOMER;
    }

    private int resolveParticipantType(WeComConversationMember member, String ownerUserId) {
        return ownerUserId != null && ownerUserId.equals(member.getUserId()) ? PARTICIPANT_TYPE_OWNER : PARTICIPANT_TYPE_MEMBER;
    }
}
