package com.snack.server.module.chat.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.snack.server.common.ResultCode;
import com.snack.server.exception.BusinessException;
import com.snack.server.module.admin.entity.Admin;
import com.snack.server.module.admin.mapper.AdminMapper;
import com.snack.server.module.chat.dto.req.ChatSessionPageReq;
import com.snack.server.module.chat.entity.ChatSession;
import com.snack.server.module.chat.enums.ChatSessionStatusEnum;
import com.snack.server.module.chat.mapper.ChatSessionMapper;
import com.snack.server.module.chat.service.ChatSessionService;
import com.snack.server.module.chat.vo.ChatSessionDetailVO;
import com.snack.server.module.chat.vo.ChatSessionVO;
import com.snack.server.module.user.entity.User;
import com.snack.server.module.user.mapper.UserMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * 客服会话服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class ChatSessionServiceImpl implements ChatSessionService {

    private static final DateTimeFormatter SESSION_NO_DATE = DateTimeFormatter.ofPattern("yyyyMMdd");

    private final ChatSessionMapper chatSessionMapper;
    private final UserMapper userMapper;
    private final AdminMapper adminMapper;

    // ==================== 用户侧 ====================

    @Override
    public ChatSession getOrCreateActiveSessionForUser(Long userId) {
        ChatSession existing = chatSessionMapper.selectOne(
                new LambdaQueryWrapper<ChatSession>()
                        .eq(ChatSession::getUserId, userId)
                        .in(ChatSession::getStatus,
                                ChatSessionStatusEnum.WAITING.getCode(),
                                ChatSessionStatusEnum.PROCESSING.getCode())
                        .orderByDesc(ChatSession::getId)
                        .last("LIMIT 1")
        );
        if (existing != null) {
            return existing;
        }
        return createOrRestoreForUser(userId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChatSession createOrRestoreForUser(Long userId) {
        ChatSession session = new ChatSession();
        session.setSessionNo(generateSessionNo());
        session.setUserId(userId);
        session.setStatus(ChatSessionStatusEnum.WAITING.getCode());
        session.setUserUnread(0);
        session.setAdminUnread(0);
        session.setUserSeq(0L);
        session.setAdminSeq(0L);
        chatSessionMapper.insert(session);
        log.info("[chat] 用户 {} 发起新会话 sessionId={}", userId, session.getId());
        return session;
    }

    // ==================== 客服侧 ====================

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChatSession acceptByAdmin(Long sessionId, Long adminId, String adminName) {
        ChatSession session = mustGet(sessionId);
        if (session.getStatus() != null
                && session.getStatus() == ChatSessionStatusEnum.CLOSED.getCode()) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "会话已关闭，无法接管");
        }
        session.setAdminId(adminId);
        // 待处理 -> 处理中
        if (session.getStatus() == null
                || session.getStatus() == ChatSessionStatusEnum.WAITING.getCode()) {
            session.setStatus(ChatSessionStatusEnum.PROCESSING.getCode());
        }
        chatSessionMapper.updateById(session);
        log.info("[chat] 客服 {} 接管会话 sessionId={}", adminName, sessionId);
        return session;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ChatSession transferToAdmin(Long sessionId, Long fromAdminId, Long toAdminId) {
        if (toAdminId == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "目标客服不能为空");
        }
        if (fromAdminId != null && fromAdminId.equals(toAdminId)) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "不能转接给自己");
        }
        ChatSession session = mustGet(sessionId);
        if (session.getAdminId() != null && !session.getAdminId().equals(fromAdminId)) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "该会话不属于你，无法转接");
        }
        session.setAdminId(toAdminId);
        chatSessionMapper.updateById(session);
        log.info("[chat] 客服 {} 转接会话 {} 给客服 {}", fromAdminId, sessionId, toAdminId);
        return session;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void closeSession(Long sessionId, Long operatorId, boolean isAdmin) {
        ChatSession session = mustGet(sessionId);
        if (session.getStatus() != null
                && session.getStatus() == ChatSessionStatusEnum.CLOSED.getCode()) {
            return; // 幂等
        }
        session.setStatus(ChatSessionStatusEnum.CLOSED.getCode());
        session.setCloseTime(LocalDateTime.now());
        chatSessionMapper.updateById(session);
        log.info("[chat] 会话 {} 被{}关闭（operator={}）", sessionId, isAdmin ? "客服" : "用户", operatorId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateStatus(Long sessionId, Integer status, Long operatorId) {
        if (status == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR);
        }
        ChatSessionStatusEnum target = ChatSessionStatusEnum.of(status);
        if (target == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "未知状态");
        }
        ChatSession session = mustGet(sessionId);
        session.setStatus(status);
        if (target == ChatSessionStatusEnum.CLOSED) {
            session.setCloseTime(LocalDateTime.now());
        }
        chatSessionMapper.updateById(session);
        log.info("[chat] 会话 {} 状态更新为 {}（operator={}）", sessionId, target.getDesc(), operatorId);
    }

    // ==================== 查询 ====================

    @Override
    public Page<ChatSessionVO> pageSessions(ChatSessionPageReq req, Long currentAdminId) {
        Page<ChatSession> page = new Page<>(req.getCurrent(), req.getSize());
        LambdaQueryWrapper<ChatSession> wrapper = new LambdaQueryWrapper<ChatSession>()
                .eq(req.getStatus() != null, ChatSession::getStatus, req.getStatus())
                .eq(req.getMine() != null && req.getMine() == 1
                        && currentAdminId != null, ChatSession::getAdminId, currentAdminId)
                .orderByDesc(ChatSession::getLastTime, ChatSession::getId);

        Page<ChatSession> result = chatSessionMapper.selectPage(page, wrapper);
        Page<ChatSessionVO> voPage = new Page<>(result.getCurrent(), result.getSize(), result.getTotal());

        List<ChatSession> records = result.getRecords();
        if (CollUtil.isEmpty(records)) {
            voPage.setRecords(Collections.emptyList());
            return voPage;
        }

        // 批量查用户 / 客服
        List<Long> userIds = records.stream().map(ChatSession::getUserId).distinct().toList();
        List<Long> adminIds = records.stream().map(ChatSession::getAdminId)
                .filter(java.util.Objects::nonNull).distinct().toList();
        Map<Long, User> userMap = userIds.isEmpty() ? Collections.emptyMap()
                : userMapper.selectList(
                        new LambdaQueryWrapper<User>().in(User::getId, userIds)).stream()
                .collect(Collectors.toMap(User::getId, Function.identity()));
        Map<Long, Admin> adminMap = adminIds.isEmpty() ? Collections.emptyMap()
                : adminMapper.selectList(
                        new LambdaQueryWrapper<Admin>().in(Admin::getId, adminIds)).stream()
                .collect(Collectors.toMap(Admin::getId, Function.identity()));

        // 关键字过滤（用户名 / 会话编号）发生在拿到 VO 之后
        String keyword = StrUtil.trimToEmpty(req.getKeyword()).toLowerCase();
        List<ChatSessionVO> vos = records.stream()
                .map(s -> toVO(s, userMap, adminMap, true))
                .filter(v -> keyword.isEmpty()
                        || (v.getUsername() != null && v.getUsername().toLowerCase().contains(keyword))
                        || (v.getSessionNo() != null && v.getSessionNo().toLowerCase().contains(keyword)))
                .toList();
        voPage.setRecords(vos);
        return voPage;
    }

    @Override
    public ChatSessionDetailVO getDetail(Long sessionId) {
        ChatSession session = mustGet(sessionId);
        ChatSessionVO vo = toVO(session, true);
        ChatSessionDetailVO detail = new ChatSessionDetailVO();
        detail.setSession(vo);
        detail.setUserSeq(session.getUserSeq());
        detail.setAdminSeq(session.getAdminSeq());
        detail.setCloseTime(session.getCloseTime());
        return detail;
    }

    @Override
    public ChatSessionVO toVO(ChatSession session) {
        return toVO(session, false);
    }

    private ChatSessionVO toVO(ChatSession session, boolean adminView) {
        return toVO(session, Collections.emptyMap(), Collections.emptyMap(), adminView);
    }

    private ChatSessionVO toVO(ChatSession session,
                               Map<Long, User> userMap,
                               Map<Long, Admin> adminMap,
                               boolean adminView) {
        if (session == null) return null;
        ChatSessionVO vo = new ChatSessionVO();
        BeanUtil.copyProperties(session, vo);

        User u = userMap.get(session.getUserId());
        if (u != null) {
            vo.setUsername(u.getUsername());
            vo.setNickname(u.getNickname());
            vo.setAvatar(u.getAvatar());
        }
        if (session.getAdminId() != null) {
            Admin a = adminMap.get(session.getAdminId());
            if (a != null) {
                vo.setAdminName(a.getNickname() != null ? a.getNickname() : a.getUsername());
            }
        }
        ChatSessionStatusEnum statusEnum = ChatSessionStatusEnum.of(session.getStatus());
        if (statusEnum != null) {
            vo.setStatusText(statusEnum.getDesc());
        }
        vo.setUnreadCount(adminView ? session.getAdminUnread() : session.getUserUnread());
        return vo;
    }

    @Override
    public ChatSession getById(Long sessionId) {
        return chatSessionMapper.selectById(sessionId);
    }

    @Override
    public List<ChatSession> listByUser(Long userId) {
        return chatSessionMapper.selectList(
                new LambdaQueryWrapper<ChatSession>()
                        .eq(ChatSession::getUserId, userId)
                        .orderByDesc(ChatSession::getId)
        );
    }

    // ==================== 私有 ====================

    private ChatSession mustGet(Long sessionId) {
        ChatSession s = chatSessionMapper.selectById(sessionId);
        if (s == null) {
            throw new BusinessException(ResultCode.PARAM_ERROR.getCode(), "会话不存在");
        }
        return s;
    }

    /**
     * 会话编号生成：CS + yyyyMMdd + 4 位当日自增序号（按 id 计数粗略实现）
     */
    private String generateSessionNo() {
        String date = LocalDate.now().format(SESSION_NO_DATE);
        Long count = chatSessionMapper.selectCount(
                new LambdaQueryWrapper<ChatSession>()
                        .likeRight(ChatSession::getSessionNo, "CS" + date)
        );
        return String.format("CS%s%04d", date, count + 1);
    }
}
