package org.dromara.main.service.impl;

import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.github.linpeilie.Converter;
import lombok.RequiredArgsConstructor;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.core.service.OssService;
import org.dromara.common.oss.core.OssClient;
import org.dromara.common.oss.entity.UploadResult;
import org.dromara.common.oss.factory.OssFactory;
import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.main.domain.CsMessage;
import org.dromara.main.domain.CsSession;
import org.dromara.main.domain.bo.CsMessageBo;
import org.dromara.main.domain.vo.CsMessageVo;
import org.dromara.main.mapper.CsMessageMapper;
import org.dromara.main.mapper.CsSeatConfigMapper;
import org.dromara.main.mapper.CsSessionMapper;
import org.dromara.main.mapper.MainCompanyApplyMapper;
import org.dromara.main.service.ICsMessageService;
import org.dromara.main.service.ICsSessionService;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import java.io.InputStream;
import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;
import org.dromara.system.domain.SysUser;
import org.dromara.system.mapper.SysUserMapper;
import org.dromara.main.domain.CsSeatConfig;
import org.dromara.main.domain.MainCompanyApply;

@RequiredArgsConstructor
@Service
public class CsMessageServiceImpl implements ICsMessageService {

    private final CsMessageMapper baseMapper;
    private final CsSessionMapper sessionMapper;
    private final CsSeatConfigMapper seatConfigMapper;
    private final MainCompanyApplyMapper mainCompanyApplyMapper;
    private final OssService ossService;
    private final SysUserMapper sysUserMapper;
    private final ICsSessionService sessionService;
    private final Converter converter;
    private final SimpMessagingTemplate messagingTemplate;

    @Override
    public TableDataInfo<CsMessageVo> queryHistoryMessages(Long sessionId, Long beforeMsgId, PageQuery pageQuery) {
        LambdaQueryWrapper<CsMessage> lqw = Wrappers.lambdaQuery();
        lqw.eq(CsMessage::getSessionId, sessionId);
        lqw.eq(CsMessage::getStatus, 1);
        if (beforeMsgId != null) {
            lqw.lt(CsMessage::getId, beforeMsgId);
        }
        lqw.orderByDesc(CsMessage::getSendTime);

        Page<CsMessage> page = baseMapper.selectPage(pageQuery.build(), lqw);
        Page<CsMessageVo> voPage = new Page<>(page.getCurrent(), page.getSize(), page.getTotal());
        List<CsMessageVo> records = converter.convert(page.getRecords(), CsMessageVo.class);
        voPage.setRecords(enrichMessageVos(records, sessionMapper.selectById(sessionId)));
        return TableDataInfo.build(voPage);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CsMessageVo sendTextMessage(CsMessageBo bo) {
        Long senderId = resolveSenderId(bo.getSenderId());
        CsMessage message = new CsMessage();
        message.setSessionId(bo.getSessionId());
        message.setMsgNo(bo.getMsgNo());
        message.setSenderId(senderId);
        message.setSenderType(resolveSenderType(bo.getSessionId(), senderId));
        message.setMsgType(bo.getMsgType());
        message.setContent(bo.getContent());
        message.setStatus(1);
        message.setIsRead(0);
        message.setSendTime(LocalDateTime.now());

        baseMapper.insert(message);
        sessionService.updateLastMessage(bo.getSessionId(),
            StrUtil.sub(bo.getContent(), 0, 100));

        CsMessageVo vo = converter.convert(message, CsMessageVo.class);
        messagingTemplate.convertAndSend("/topic/session/" + bo.getSessionId(), vo);
        return vo;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CsMessageVo sendImageMessage(Long sessionId, String msgNo, Long senderId, MultipartFile file) {
        try {
            Long actualSenderId = resolveSenderId(senderId);
            OssClient ossClient = OssFactory.instance();
            String fileName = file.getOriginalFilename();
            InputStream inputStream = file.getInputStream();
            Long fileSize = file.getSize();
            String contentType = file.getContentType();

            // 生成存储路径
            String key = "chat/images/" + System.currentTimeMillis() + "_" + fileName;

            // 上传文件
            UploadResult uploadResult = ossClient.upload(inputStream, key, fileSize, contentType);

            CsMessage message = new CsMessage();
            message.setSessionId(sessionId);
            message.setMsgNo(msgNo);
            message.setSenderId(actualSenderId);
            message.setSenderType(resolveSenderType(sessionId, actualSenderId));
            message.setMsgType("image");
            message.setFileUrl(uploadResult.getUrl());
            message.setFileName(fileName);
            message.setFileSize(fileSize);
            message.setFileType(contentType);
            message.setStatus(1);
            message.setIsRead(0);
            message.setSendTime(LocalDateTime.now());

            baseMapper.insert(message);
            sessionService.updateLastMessage(sessionId, "[图片]");

            CsMessageVo vo = converter.convert(message, CsMessageVo.class);
            messagingTemplate.convertAndSend("/topic/session/" + sessionId, vo);
            return vo;
        } catch (Exception e) {
            throw new RuntimeException("上传图片失败: " + e.getMessage(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CsMessageVo sendFileMessage(Long sessionId, String msgNo, Long senderId, MultipartFile file) {
        try {
            Long actualSenderId = resolveSenderId(senderId);
            OssClient ossClient = OssFactory.instance();
            String fileName = file.getOriginalFilename();
            InputStream inputStream = file.getInputStream();
            Long fileSize = file.getSize();
            String contentType = file.getContentType();

            // 生成存储路径
            String key = "chat/files/" + System.currentTimeMillis() + "_" + fileName;

            // 上传文件
            UploadResult uploadResult = ossClient.upload(inputStream, key, fileSize, contentType);

            CsMessage message = new CsMessage();
            message.setSessionId(sessionId);
            message.setMsgNo(msgNo);
            message.setSenderId(actualSenderId);
            message.setSenderType(resolveSenderType(sessionId, actualSenderId));
            message.setMsgType("file");
            message.setFileUrl(uploadResult.getUrl());
            message.setFileName(fileName);
            message.setFileSize(fileSize);
            message.setFileType(contentType);
            message.setStatus(1);
            message.setIsRead(0);
            message.setSendTime(LocalDateTime.now());

            baseMapper.insert(message);
            sessionService.updateLastMessage(sessionId, "[文件]" + fileName);

            CsMessageVo vo = converter.convert(message, CsMessageVo.class);
            messagingTemplate.convertAndSend("/topic/session/" + sessionId, vo);
            return vo;
        } catch (Exception e) {
            throw new RuntimeException("上传文件失败: " + e.getMessage(), e);
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public CsMessageVo sendJobCard(CsMessageBo bo) {
        Long senderId = resolveSenderId(bo.getSenderId());
        CsMessage message = new CsMessage();
        message.setSessionId(bo.getSessionId());
        message.setMsgNo(bo.getMsgNo());
        message.setSenderId(senderId);
        message.setSenderType(resolveSenderType(bo.getSessionId(), senderId));
        message.setMsgType("job_card");
        message.setPayload(JSONUtil.toJsonStr(bo.getPayload()));
        message.setStatus(1);
        message.setIsRead(0);
        message.setSendTime(LocalDateTime.now());

        baseMapper.insert(message);
        sessionService.updateLastMessage(bo.getSessionId(), "[岗位推荐]");

        CsMessageVo vo = converter.convert(message, CsMessageVo.class);
        messagingTemplate.convertAndSend("/topic/session/" + bo.getSessionId(), vo);
        return vo;
    }

    @Override
    public Boolean markAsRead(Long sessionId, Long lastReadMsgId) {
        baseMapper.update(null,
            Wrappers.lambdaUpdate(CsMessage.class)
                .set(CsMessage::getIsRead, 1)
                .eq(CsMessage::getSessionId, sessionId)
                .le(CsMessage::getId, lastReadMsgId)
                .eq(CsMessage::getIsRead, 0));

        sessionService.clearUnreadCount(sessionId);
        return true;
    }

    @Override
    public Boolean recallMessage(Long msgId) {
        CsMessage update = new CsMessage();
        update.setId(msgId);
        update.setStatus(2);
        return baseMapper.updateById(update) > 0;
    }

    private Long resolveSenderId(Long senderId) {
        return senderId != null ? senderId : LoginHelper.getUserId();
    }

    private Integer resolveSenderType(Long sessionId, Long senderId) {
        if (senderId == null || sessionId == null) {
            return 1;
        }
        CsSession session = sessionMapper.selectById(sessionId);
        if (session != null && session.getWaiterId() != null && session.getWaiterId().equals(senderId)) {
            return 2;
        }
        return 1;
    }

    private List<CsMessageVo> enrichMessageVos(List<CsMessageVo> records, CsSession session) {
        if (records == null || records.isEmpty()) {
            return records;
        }
        Map<Long, SysUser> waiterMap = loadWaiterMap(records);
        CsSeatConfig seat = session != null && session.getSeatId() != null ? seatConfigMapper.selectById(session.getSeatId()) : null;
        MainCompanyApply companyApply = loadCompanyApply(session);
        records.forEach(item -> enrichMessageVo(item, session, seat, companyApply, waiterMap));
        return records;
    }

    private void enrichMessageVo(CsMessageVo vo, CsSession session, CsSeatConfig seat, MainCompanyApply companyApply, Map<Long, SysUser> waiterMap) {
        if (vo == null) {
            return;
        }
        if (Objects.equals(vo.getSenderType(), 2)) {
            SysUser waiter = waiterMap.get(vo.getSenderId());
            if (waiter != null) {
                vo.setSenderName(StrUtil.blankToDefault(waiter.getNickName(), waiter.getUserName()));
                vo.setSenderAvatar(seat != null ? seat.getAvatar() : null);
                vo.setSenderRole("waiter");
                return;
            }
        }
        if (Objects.equals(vo.getSenderType(), 3)) {
            vo.setSenderName("系统消息");
            vo.setSenderRole("system");
            return;
        }
        if (session != null) {
            vo.setSenderName(session.getFromUserName());
            vo.setSenderAvatar(resolveFromUserAvatar(session, companyApply));
            vo.setSenderRole("user");
        }
    }

    private MainCompanyApply loadCompanyApply(CsSession session) {
        if (session == null || session.getSessionType() == null || session.getSessionType() != 2) {
            return null;
        }
        return mainCompanyApplyMapper.selectOne(
            Wrappers.lambdaQuery(MainCompanyApply.class)
                .and(wrapper -> wrapper
                    .eq(MainCompanyApply::getTenantId, String.valueOf(session.getFromUserId()))
                    .or()
                    .eq(MainCompanyApply::getId, session.getFromUserId())
                    .or()
                    .eq(StrUtil.isNotBlank(session.getFromUserName()), MainCompanyApply::getCompanyName, session.getFromUserName()))
                .orderByDesc(MainCompanyApply::getCreateTime)
                .last("LIMIT 1")
        );
    }

    private String resolveFromUserAvatar(CsSession session, MainCompanyApply companyApply) {
        if (session != null && StrUtil.isNotBlank(session.getFromUserAvatar())) {
            return session.getFromUserAvatar();
        }
        if (companyApply != null && companyApply.getAvatar() != null) {
            return ossService.selectUrlByIds(companyApply.getAvatar().toString());
        }
        return null;
    }

    private Map<Long, SysUser> loadWaiterMap(List<CsMessageVo> records) {
        List<Long> waiterIds = records.stream()
            .filter(item -> Objects.equals(item.getSenderType(), 2))
            .map(CsMessageVo::getSenderId)
            .filter(Objects::nonNull)
            .distinct()
            .collect(Collectors.toList());
        if (waiterIds.isEmpty()) {
            return Collections.emptyMap();
        }
        return sysUserMapper.selectBatchIds(waiterIds).stream()
            .collect(Collectors.toMap(SysUser::getUserId, Function.identity()));
    }
}
