package com.sikey.wa04.business.application.service.user;


import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.http.HttpStatus;
import cn.hutool.json.JSONUtil;
import com.sikey.wa04.business.application.service.message.ContactMessageService;
import com.sikey.wa04.business.domain.adapter.*;
import com.sikey.wa04.business.domain.entity.*;
import com.sikey.wa04.business.domain.usecase.ChildIdFactory;
import com.sikey.wa04.business.infrastructure.dto.model.ChildModel;
import com.sikey.wa04.business.infrastructure.dto.model.ChildRefModel;
import com.sikey.wa04.business.infrastructure.dto.model.ContactModel;
import com.sikey.wa04.business.infrastructure.dto.model.UserModel;
import com.sikey.wa04.business.infrastructure.dto.request.contact.CreateContactRequest;
import com.sikey.wa04.business.infrastructure.dto.request.contact.DeleteContactRequest;
import com.sikey.wa04.business.infrastructure.dto.request.contact.GetContactRequest;
import com.sikey.wa04.business.infrastructure.dto.request.contact.UpdateContactRequest;
import com.sikey.wa04.business.infrastructure.dto.request.messaging.ContactMessageRequest;
import com.sikey.wa04.business.infrastructure.dto.response.contact.GetContactResponse;
import com.sikey.wa04.business.infrastructure.repository.*;
import com.sikey.wa04.common.dto.header.GenericManufacturerHeader;
import com.sikey.wa04.common.enums.ContactRole;
import com.sikey.wa04.common.enums.GroupMemberRole;
import com.sikey.wa04.common.enums.IdentityEnum;
import com.sikey.wa04.common.enums.UserType;
import com.sikey.wa04.common.exception.BusinessException;
import com.sikey.wa04.common.exception.errors.Errors;
import com.sikey.wa04.common.interceptor.CurrentRequestUser;
import jakarta.annotation.Resource;
import jakarta.transaction.Transactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;

import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 联系人
 *
 * @author luoyangwei
 */
@Slf4j
@Service
public class ContactsService {

    @Resource
    private GroupAdapter groupAdapter;

    @Resource
    private GroupMemberAdapter groupMemberAdapter;

    @Resource
    private ContactRepository contactRepository;

    @Resource
    private UserRepository userRepository;

    @Resource
    private UserAdapter userAdapter;

    @Resource
    private ChildRepository childRepository;

    @Resource
    private ChildRefRepository childRefRepository;

    @Resource
    private MessageCenterAdapter messageCenterAdapter;

    @Resource
    private DeviceConfigAdapter deviceConfigAdapter;

    @Qualifier("myTaskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor myTaskExecutor;

    @Resource
    private ContactMessageService contactMessageService;

    @Resource
    private MessageRecvRepository messageRecvRepository;

    /**
     * 加入家庭群
     *
     * @param userId  要加入的用户ID
     * @param childId 属于这个小孩ID的家庭群
     * @throws BusinessException 家庭群聊不存在时出现
     */
    private void joinFamilyGroup(UserId userId, ChildId childId) {
        Group group = groupAdapter.findByChildId(childId);
        if (Objects.isNull(group)) {
            throw Errors.FAMILY_GROUPS_DO_NOT_EXIST.getException();
        }

        List<GroupMember> members = groupMemberAdapter.findMembers(group.getId());
        if (members.stream().noneMatch(member -> member.getRefId().equals(userId.getId()))) {
            GroupMember member = new GroupMember().setGroupId(group.getId().getId()).setRefId(userId.getId()).setType(UserType.USER).setRole(GroupMemberRole.EVERYMAN);
            groupMemberAdapter.join(group.getId(), member);
        }
    }


    /**
     * 预注册用户, 已经存在数据将不注册
     *
     * @return 用户ID
     */
    private UserModel preRegistrationUser(PhoneNumber phoneNumber) {
        UserModel userModel = userRepository.findByPhoneNumberAndAreaCode(phoneNumber.number(), phoneNumber.areaCode());
        if (userModel == null) {
            userModel = new UserModel();
            userModel.setPhoneNumber(phoneNumber.number()).setId(UUID.randomUUID().toString()).setAreaCode(phoneNumber.areaCode());
            userModel.setIsRegistered(false);
            userRepository.save(userModel);
        }
        return userModel;
    }

    /**
     * 创建通讯录联系人
     * 管理员，家人，其它创建通讯录联系人
     *
     * @param request                   request
     * @param genericManufacturerHeader header
     * @throws Exception 异常
     */
    @Transactional
    public void createMember(CreateContactRequest request, GenericManufacturerHeader genericManufacturerHeader) throws Exception {
        log.info("[创建通讯录联系人]请求参数: {}", JSONUtil.toJsonPrettyStr(request));
        PhoneNumber phoneNumber = new PhoneNumber(request.phoneNumber(), request.areaCode());

        String currentUserId = CurrentRequestUser.get().getId();

        UserModel userModel = preRegistrationUser(phoneNumber);
        UserId userId = new UserId(userModel.getId());

        // 校验联系人
        checkConcat(currentUserId, request.cid(), userId.getId(), request.phoneNumber(), request.areaCode(), null);

        ContactRole role = ContactRole.valueOf(request.role());
        if (ContactRole.FAMILY.equals(role)) {
            joinFamilyGroup(userId, ChildIdFactory.create(request.cid()));
        }

        // Initialize ref information
        ChildRefModel refModel = new ChildRefModel();
        refModel.setUserId(userId.getId());
        refModel.setChildId(request.cid());

        // 新增身份
        int identity = 0;
        if (role.getValue().intValue() == ContactRole.FAMILY.getValue()) {
            identity = IdentityEnum.IDENTITY_FAMILY.getValue().intValue();
        } else if (role.getValue().intValue() == ContactRole.OTHER.getValue() || role.getValue().intValue() == ContactRole.FRIEND.getValue()) {
            identity = IdentityEnum.IDENTITY_OTHER.getValue().intValue();
        }
        refModel.setIdentity(identity);

        childRefRepository.save(refModel);

        ContactModel contactModel = new ContactModel();
        contactModel.setChildId(request.cid());
        contactModel.setRefId(userId.getId());
        contactModel.setRefType(UserType.USER.getValue());
        contactModel.setRefRole(role.getValue());
        contactModel.setRemarkName(request.appellation());
        contactModel.setRemarkAvatar(request.avatar());
        contactRepository.save(contactModel);

        // 消息中心
        myTaskExecutor.execute(() -> {
            /*Optional<ChildModel> childModel = childRepository.findById(request.cid());
            if (childModel.isPresent()) {
                ChildModel cm = childModel.get();
                // 加入消息到消息中心
                MessageCenter messageCenter = new MessageCenter();
                messageCenter.setCid(cm.getId());
                messageCenter.setMessageType(MessageCenterType.MESSAGE_CENTER_TYPE_CONTACTS.getValue());
                messageCenter.setMessageParams(Map.of("name", cm.getChildName(), "friendName", request.appellation()));
                messageCenterAdapter.save(messageCenter, MessageCenterType.MESSAGE_CENTER_TYPE_CONTACTS);
            }*/

            // 变动通讯录消息通知
            ContactMessageRequest contactMessageRequest = new ContactMessageRequest();
            contactMessageRequest.setCid(request.cid());
            contactMessageRequest.setRole(request.role());
            contactMessageRequest.setHeader(genericManufacturerHeader);
            contactMessageRequest.setUserId(userId.getId());
            contactMessageService.contactMessageChange(contactMessageRequest, currentUserId);
        });


    }

    /**
     * 校验联系人
     *
     * @param currentUserId 当前登录用户
     * @param childId       儿童id
     * @param userId        新增加的用户
     * @param phoneNumber   新增用户手机号
     * @param areaCode      国家编码
     * @param refId         编辑用户id
     * @return
     */
    private void checkConcat(String currentUserId, String childId, String userId, String phoneNumber, String areaCode, String refId) {
        // 校验儿童家长手机号是否使用
        Optional<ChildModel> childModel = childRepository.findById(childId);
        log.info("[校验联系人]校验儿童家长手机号，currentUserId：{}，cId：{}，返回数据：{}", currentUserId, childId, JSONUtil.toJsonPrettyStr(childModel));
        // 查询儿童通讯录
        List<ContactModel> contactModelList = contactRepository.findByChildId(childId);

        if (childModel.isPresent() && StringUtils.equals(childModel.get().getPhoneNumber(), phoneNumber)) {
            throw new BusinessException(HttpStatus.HTTP_INTERNAL_ERROR, "不能和宝贝自己的手机号一样");
        }

        if (CollectionUtil.isNotEmpty(contactModelList)) {
            List<String> refIdList = contactModelList.stream().map(ContactModel::getRefId).collect(Collectors.toList());
            List<User> userList = userAdapter.findByUserIdIn(refIdList);

            if (CollectionUtil.isNotEmpty(userList)) {
                userList = userList.stream().filter(u -> StringUtils.equals(u.getPhoneNumber().number(), phoneNumber)).toList();
                if (CollectionUtil.isNotEmpty(userList) && !StringUtils.equals(userId, refId)) {
                    log.info("[校验联系人]查询用户，userId：{}，refId：{}，返回数据：{}", userId, refId, JSONUtil.toJsonPrettyStr(contactModelList));
                    throw new BusinessException(HttpStatus.HTTP_INTERNAL_ERROR, "联系人已存在");
                }
            }

        }

    }

    /**
     * 更新通讯录联系人
     *
     * @param request                   request
     * @param genericManufacturerHeader header
     * @throws Exception 异常
     */
    @Transactional
    public void updateMember(UpdateContactRequest request, GenericManufacturerHeader genericManufacturerHeader) throws Exception {
        // log.info("[更新通讯录联系人]角色：{}", request.role());
        PhoneNumber phoneNumber = new PhoneNumber(request.phoneNumber(), request.areaCode());

        String currentUserId = CurrentRequestUser.get().getId();

        // Verify whether the mobile phone number is used by other users
        // Add one more verification. The user's mobile phone number can be the same as his own,
        // But it cannot be the same as someone else's.
        // 不能和通讯录里已有的手机号一样
        UserModel userModel = preRegistrationUser(phoneNumber);
        UserId newUserId = new UserId(userModel.getId());

        // 校验联系人
        checkConcat(currentUserId, request.cid(), newUserId.getId(), request.phoneNumber(), request.areaCode(), request.refId());

        // 不能和该宝贝自己的手机号一样
        /*childRepository.findById(request.cid()).ifPresent(child -> {
            if (StringUtils.equals(child.getPhoneNumber(), phoneNumber.number())) {
                throw Errors.PHONE_NUMBER_EXIST.getException();
            }
        });*/

        // UserModel userModel = preRegistrationUser(phoneNumber);

        UserId userId = new UserId(newUserId.getId());

        ChildId childId = ChildIdFactory.create(request.cid());

        // Group member permissions control whether to join family group chat
        // Modify to family: Add the user to the family group chat
        // Modify to other: Kick user from group chat
        ContactRole role = ContactRole.valueOf(request.role());
        if (ContactRole.FAMILY.equals(role) && StringUtils.equals(userId.getId(), request.refId())) {
            joinFamilyGroup(userId, childId);
        }

        if ((ContactRole.OTHER.equals(role) || ContactRole.FRIEND.equals(role)) && StringUtils.equals(userId.getId(), request.refId())) {
            Group group = groupAdapter.findByChildId(childId);
            groupMemberAdapter.leave(group.getId(), userId);
            // log.info("[更新通讯录联系人]离开组，{}", JSONUtil.toJsonPrettyStr(group));
        }

        List<ContactModel> contactModels = contactRepository.findByChildId(childId.getId());
        ContactModel contactModel = contactModels.stream().filter(mod -> mod.getRefId().equals(request.refId())).findFirst().orElseThrow();
        contactModel.setRefRole(role.getValue());
        contactModel.setRemarkName(request.appellation());
        contactModel.setRemarkAvatar(request.avatar());
        //  log.info("[更新通讯录联系人]联系人：{}", JSONUtil.toJsonPrettyStr(contactModel));
        contactRepository.save(contactModel);

        // 更新儿童关系
        if (!StringUtils.equals(userModel.getId(), request.refId())) {
            childRefRepository.updateByUserIdAndChildId(request.refId(), request.cid(), newUserId.getId(), Instant.now());
            contactRepository.updateByRefIdAndChildId(request.cid(), request.refId(), newUserId.getId(), Instant.now());

            Group group = groupAdapter.findByChildId(childId);
            groupMemberAdapter.leave(group.getId(), new UserId(request.refId()));
            joinFamilyGroup(userId, childId);
            log.info("[更新通讯录联系人]更新儿童关系成功，待添加通讯录联系人：{}，请求参数：{}", JSONUtil.toJsonPrettyStr(userModel), JSONUtil.toJsonPrettyStr(request));
        }

        myTaskExecutor.execute(() -> {
            // 变动通讯录消息通知
            ContactMessageRequest contactMessageRequest = new ContactMessageRequest();
            contactMessageRequest.setCid(request.cid());
            contactMessageRequest.setRole(request.role());
            contactMessageRequest.setHeader(genericManufacturerHeader);
            contactMessageRequest.setUserId(userId.getId());
            contactMessageService.contactMessageChange(contactMessageRequest, currentUserId);
        });

    }

    /**
     * Verify whether the mobile phone number is used by other children
     * 就当前看下来不需要验证手机号码是否和小孩的手机号码重复，但不确定有没有其他的问题，方法先留着
     *
     * @param phoneNumber 手机号实体
     * @throws Exception {@link Errors#PHONE_NUMBER_EXIST} 手机号已存在异常
     */
    private void checkPhoneNumberByChildren(PhoneNumber phoneNumber) throws Exception {
        if (!childRepository.findByPhoneNumberAndAreaCode(phoneNumber.number(), phoneNumber.areaCode()).isEmpty()) {
            throw Errors.PHONE_NUMBER_EXIST.getException();
        }
    }

    /**
     * 删除通讯录联系人
     *
     * @param request request
     * @throws Exception 异常
     */
    @Transactional
    public void deleteMember(DeleteContactRequest request, GenericManufacturerHeader genericManufacturerHeader) throws Exception {
        log.info("[删除家庭成员] userId:{}, childId:{}", request.refId(), request.cid());
        // 获取通讯录联系人角色
        ContactModel contactModel = contactRepository.findByChildIdAndRefId(request.cid(), request.refId());
        Integer refRole = Optional.ofNullable(contactModel).orElseThrow(() -> new BusinessException(HttpStatus.HTTP_INTERNAL_ERROR, "用户不存在")).getRefRole();

        // 彼此互删通讯录
        contactRepository.deleteByChildIdAndRefId(request.cid(), request.refId());

        // 彼此互删通讯录
        contactRepository.deleteByChildIdAndRefId(request.refId(), request.cid());

        // 删除小孩与家庭成员的关系
        childRefRepository.deleteByChildIdAndUserId(request.cid(), request.refId());

        // 删除群组成员
        Group group = groupAdapter.findByChildId(ChildIdFactory.create(request.cid()));
        groupMemberAdapter.leave(group.getId(), new UserId(request.refId()));

        // 删除私聊消息
        messageRecvRepository.deleteByRecvIdAndSendId(request.refId(), request.cid());
        // 删除群聊消息
        messageRecvRepository.deleteByRecvIdAndGroupId(request.refId(), group.getId().getId());

        String currentUserId = CurrentRequestUser.get().getId();

        myTaskExecutor.execute(() -> {
            // 变动通讯录消息通知
            ContactMessageRequest contactMessageRequest = new ContactMessageRequest();
            contactMessageRequest.setCid(request.cid());
            contactMessageRequest.setRole(refRole);
            contactMessageRequest.setHeader(genericManufacturerHeader);
            contactMessageRequest.setUserId(request.refId());
            contactMessageService.contactMessageChange(contactMessageRequest, currentUserId);
        });
    }

    /**
     * 获取通讯录联系人
     *
     * @param request      request
     * @param isWristwatch 是否手表
     * @return 通讯录联系人
     * @throws Exception 异常
     */
    public GetContactResponse get(GetContactRequest request, GenericManufacturerHeader genericManufacturerHeader, Boolean isWristwatch) throws Exception {
        List<ContactModel> contactModels = contactRepository.findByChildIdOrderByIdAsc(request.cid());
        log.info("[获取通讯录联系人]通过儿童编码：{}，获取儿童信息：{}", request.cid(), JSONUtil.toJsonPrettyStr(contactModels));

        // 已注册的家长用户
        Set<String> isRegisteredUserIdList = isRegisteredUserIdList(contactModels);

        List<GetContactResponse.GetContactResult> contacts = new ArrayList<>();
        for (ContactModel contactModel : contactModels) {
            String avatar;
            if (isWristwatch && StringUtils.equalsAnyIgnoreCase(genericManufacturerHeader.getDeviceModel(), "W301H", "N8", "N8#CMCC", "N8#CTCC")) {
                avatar = convertUrlSuffix(contactModel.getRemarkAvatar());
            } else {
                avatar = contactModel.getRemarkAvatar();
            }
            GetContactResponse.GetContactResult result = new GetContactResponse.GetContactResult().setCtid(contactModel.getId()).setRefId(contactModel.getRefId()).setRole(contactModel.getRefRole())
                    .setAvatar(avatar)
                    .setAppellation(contactModel.getRemarkName()).setIsVideoCall(isRegisteredUserIdList.contains(contactModel.getRefId()));

            ContactRole role = ContactRole.valueOf(contactModel.getRefRole());

            Integer refType = contactModel.getRefType();
            if (ContactRole.CHILDREN.equals(role)) {
                childRepository.findById(request.cid()).ifPresent(child -> result.setPhoneNumber(child.getPhoneNumber()).setAreaCode(child.getAreaCode()).setType(UserType.CHILD.getValue()));
            } else if (Objects.nonNull(refType)) {

                if (ContactRole.FRIEND.equals(role) && UserType.CHILD.getValue() == refType.intValue()) {
                    childRepository.findById(contactModel.getRefId()).ifPresent(child -> result.setPhoneNumber(child.getPhoneNumber()).setAreaCode(child.getAreaCode()).setType(UserType.CHILD.getValue()));
                } else if (ContactRole.FRIEND.equals(role) && UserType.USER.getValue() == refType.intValue()) {
                    userRepository.findById(contactModel.getRefId()).ifPresent(user -> result.setPhoneNumber(user.getPhoneNumber()).setAreaCode(user.getAreaCode()).setType(UserType.USER.getValue()));
                } else {
                    userRepository.findById(contactModel.getRefId()).ifPresent(user -> result.setPhoneNumber(user.getPhoneNumber()).setAreaCode(user.getAreaCode()).setType(UserType.USER.getValue()));
                }

                contacts.add(result);
            }
        }

        if (isWristwatch) {
            myTaskExecutor.execute(() -> {
                // 设备配置
                deviceConfigAdapter.deviceConfigMessageCenter(request.cid(), genericManufacturerHeader);
            });
        }

        log.info("[获取通讯录联系人]获取通讯录联系人信息：{}", JSONUtil.toJsonPrettyStr(contacts));
        return new GetContactResponse(contacts);
    }

    /**
     * 将URL中的文件后缀名替换为.bin
     *
     * @param originalUrl 原始URL
     * @return 处理后URL
     */
    private static String convertUrlSuffix(String originalUrl) {
        if (originalUrl == null || originalUrl.isEmpty()) {
            return originalUrl;
        }

        // 获取最后一个点号的位置
        int lastDotIndex = originalUrl.lastIndexOf('.');
        if (lastDotIndex == -1) {
            return originalUrl; // 没有扩展名
        }

        return originalUrl.substring(0, lastDotIndex + 1) + "bin";
    }

    /**
     * 已注册的家长用户
     *
     * @param contactModels 通讯录联系人
     * @return
     */
    private Set<String> isRegisteredUserIdList(List<ContactModel> contactModels) {
        if (CollectionUtil.isEmpty(contactModels)) {
            return Collections.emptySet();
        }

        // 家长，因为手表之间不会视频通话
        List<String> userRefIdList = contactModels.stream().filter(ct -> ct.getRefType().equals(UserType.USER.getValue())).map(ContactModel::getRefId).toList();
        List<User> findByUserIdIn = userAdapter.findByUserIdIn(userRefIdList);
        if (CollectionUtil.isNotEmpty(findByUserIdIn)) {
            return findByUserIdIn.stream().filter(User::getIsRegistered).map(User::getId).map(UserId::getId).collect(Collectors.toSet());
        }
        return Collections.emptySet();
    }


}
