package com.easyproject.clientbus.service.chat.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.UtilException;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.http.*;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.coze.openapi.client.chat.CreateChatReq;
import com.coze.openapi.client.chat.model.ChatEvent;
import com.coze.openapi.client.chat.model.ChatEventType;
import com.coze.openapi.client.connversations.CreateConversationReq;
import com.coze.openapi.client.connversations.CreateConversationResp;
import com.coze.openapi.client.connversations.message.model.Message;
import com.coze.openapi.client.connversations.message.model.MessageType;
import com.coze.openapi.service.service.CozeAPI;
import com.easyproject.clientbus.domain.dto.LikeOrStepOnDto;
import com.easyproject.clientbus.domain.vo.CozeTranscriptionResult;
import com.easyproject.clientbus.service.chat.IChatAiService;
import com.easyproject.clientbus.service.chat.IConversationChatService;
import com.easyproject.common.config.ProjectConfig;
import com.easyproject.common.constant.Constants;
import com.easyproject.common.core.domain.AjaxClientResult;
import com.easyproject.common.utils.DateUtils;
import com.easyproject.common.utils.ServletUtils;
import com.easyproject.common.utils.StringUtils;
import com.easyproject.common.utils.coze.ChatAiForCozeUtils;
import com.easyproject.common.utils.file.FileUploadUtils;
import com.easyproject.common.utils.uuid.Seq;
import com.easyproject.service.domain.aichat.BotConversation;
import com.easyproject.service.domain.aichat.BotConversationChat;
import com.easyproject.service.domain.coze.CozeBotSet;
import com.easyproject.service.domain.coze.CozeTokenSet;
import com.easyproject.service.domain.coze.CozeWorkspacesSet;
import com.easyproject.service.mapper.aichat.BotConversationChatMapper;
import com.easyproject.service.mapper.aichat.BotConversationMapper;
import com.easyproject.service.mapper.coze.CozeBotSetMapper;
import com.easyproject.service.mapper.coze.CozeWorkspacesSetMapper;
import com.easyproject.service.service.ICozeJwtService;
import io.reactivex.Flowable;
import io.reactivex.schedulers.Schedulers;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.FilenameUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Sinks;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;


import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

/**
 * 方法说明: 聊天部分
 *
 * @param
 * @author java-101
 * @return
 * @date 2025-03-23 15:11:47
 */
@Slf4j
@Service
public class ChatAiServiceImpl implements IChatAiService {

    @Resource
    private CozeWorkspacesSetMapper cozeWorkspacesSetMapper;
    @Resource
    private CozeBotSetMapper cozeBotSetMapper;
    @Resource
    private BotConversationMapper botConversationMapper;
    @Resource
    private IConversationChatService conversationChatService;

    //随机用户ID
    private String userID = UUID.randomUUID().toString();
    @Autowired
    private BotConversationChatMapper botConversationChatMapper;
    @Resource
    private ICozeJwtService cozeJwtService;

    /**
     * 方法说明: 发起消息-通过流式返回-coze平台
     *
     * @param message        用户输入的消息
     * @param conversationId 会话ID
     * @return 流式返回的消息
     * @author java-101
     * @date 2025-03-23 15:16:05
     */
    @Override
    public Flux<String> chatSendStream(String message, String conversationId) {

        // 查询系统token 使用JWT方式获取token（永久授权）
        CozeTokenSet tokenSet = cozeJwtService.getCozeTokenSetForToken();
        if (ObjectUtil.isEmpty(tokenSet)) {
            log.error("token为空，请先设置token！");
            return Flux.error(new RuntimeException("token为空，系统错误，稍后重试"));
        }

        //查询有效空间
        CozeWorkspacesSet workspacesSet = cozeWorkspacesSetMapper.selectOne(Wrappers.<CozeWorkspacesSet>lambdaQuery().eq(CozeWorkspacesSet::getStatus, 1).orderByDesc(CozeWorkspacesSet::getId).last("limit 1"));
        if (ObjectUtil.isEmpty(workspacesSet)) {
            log.error("系统错误，请稍后重试");
            return Flux.error(new RuntimeException("系统错误，请稍后重试"));
        }
        //查询机器人ID（智能体ID）
        CozeBotSet botSet = cozeBotSetMapper.selectOne(Wrappers.<CozeBotSet>lambdaQuery().eq(CozeBotSet::getStatus, 1).eq(CozeBotSet::getSpaceId, workspacesSet.getSpacesId()).orderByDesc(CozeBotSet::getId).last("limit 1"));
        if (ObjectUtil.isEmpty(botSet)) {
            log.error("系统错误，请稍后重试");
            return Flux.error(new RuntimeException("系统错误，请稍后重试"));
        }

        //处理编码问题
        try {
            message = URLUtil.decode(message, CharsetUtil.UTF_8);
        } catch (UtilException e) {
            throw new RuntimeException(e);
        }

        //初始化客户端
        CozeAPI coze = ChatAiForCozeUtils.getCozeClient(tokenSet.getPersonToken());

        //判断是否需要创建会话（确保有会话ID）
        if (ObjectUtil.isEmpty(conversationId) || conversationId.equals("null")) {
            //创建会话
            CreateConversationReq req = CreateConversationReq.builder()
                    .botID(botSet.getBotId())
                    .build();
            CreateConversationResp resp = coze.conversations().create(req);
            if (ObjectUtil.isNotEmpty(resp) && ObjectUtil.isNotEmpty(resp.getConversation())) {
                conversationId = resp.getConversation().getId();//会话ID
                //将会话创建信息，写入数据库
                BotConversation conversation = new BotConversation();
                conversation.setBotId(botSet.getBotId());
                conversation.setConversationId(conversationId);
                conversation.setConversationTile(StrUtil.subPre(StrUtil.trim(message), 20));
                conversation.setCreateTime(DateUtil.date());
                conversation.setUpdateTime(DateUtil.date());
                botConversationMapper.insert(conversation);
            }
        }

        // 处理流式返回数据
        Sinks.Many<String> sink = Sinks.many().unicast().onBackpressureBuffer();
        // 构造聊天请求
        CreateChatReq req = CreateChatReq.builder()
                .botID(botSet.getBotId())
                .userID(userID)
                .conversationID(conversationId)
                .messages(Collections.singletonList(Message.buildUserQuestionText(message)))
                .build();

        Flowable<ChatEvent> response = coze.chat().stream(req);
        String finalConversationId = conversationId;
        AtomicReference<String> chatId = new AtomicReference<>("");
        String finalMessage = message;
        response.subscribeOn(Schedulers.io()).subscribe(
                event -> {
                    if (ChatEventType.CONVERSATION_CHAT_CREATED.equals(event.getEvent())) {
                        //log.info("会话开始：{} - {}", event.getChat().getConversationID(),event.getChat().getID());
                        chatId.set(event.getChat().getID());
                        String eventJson = JSONUtil.toJsonStr(event);
                        sink.tryEmitNext(eventJson);
                    }
                    if (ChatEventType.CONVERSATION_MESSAGE_DELTA.equals(event.getEvent())) {
                        String eventJson = JSONUtil.toJsonStr(event);
                        sink.tryEmitNext(eventJson);
                    }
                    if (ChatEventType.CONVERSATION_MESSAGE_COMPLETED.equals(event.getEvent())) {
                        if (event.getMessage().getType().equals(MessageType.ANSWER)) {//智能体返回给用户的消息内容
                            /*log.info("=====>> {}" , event.getMessage().getContent());
                            log.info("会话ID：{} - 对话ID：{}", finalConversationId,chatId);*/
                            try {
                                //处理对话数据
                                BotConversationChat chat = new BotConversationChat();
                                chat.setConversationId(finalConversationId);
                                chat.setBotId(botSet.getBotId());
                                chat.setChatId(chatId.get());
                                chat.setUserId(userID);
                                chat.setRole("assistant");
                                chat.setMsgContent(StrUtil.trim(finalMessage));
                                chat.setBackMsgContent(event.getMessage().getContent());
                                chat.setEvaluate(1);
                                chat.setIsUpload(1);
                                chat.setCreateTime(DateUtil.date());
                                chat.setUpdateTime(DateUtil.date());
                                conversationChatService.asyncSaveChat(chat);
                            } catch (Exception e) {
                                log.error("保存对话数据失败：{}", e.getMessage());
                            }

                            String eventJson = JSONUtil.toJsonStr(event);
                            sink.tryEmitNext(eventJson);
                        }
                    }
                    if (ChatEventType.CONVERSATION_CHAT_COMPLETED.equals(event.getEvent())) {
                        String eventJson = JSONUtil.toJsonStr(event);
                        sink.tryEmitNext(eventJson);
                        sink.tryEmitComplete();
                    }
                },
                error -> sink.tryEmitError(error)
        );
        return sink.asFlux();
    }

    /**
     * 方法说明: 语音转文字
     *
     * @param
     * @return
     * @author java-101
     * @date 2025-03-23 19:13:31
     */
    @Override
    public AjaxClientResult transcribeAudio(MultipartFile file) throws IOException {
        String url = ChatAiForCozeUtils.getApiUrlTranscriptions();
        // 查询系统token 使用JWT方式获取token（永久授权）
        CozeTokenSet tokenSet = cozeJwtService.getCozeTokenSetForToken();
        if (ObjectUtil.isEmpty(tokenSet)) {
            log.error("token为空，请先设置token！");
            return AjaxClientResult.error("token为空，系统错误，稍后重试");
        }
        // 直接使用文件流，避免创建临时文件
        try {

            File tempFile = File.createTempFile("audio", ".mp3");
            file.transferTo(tempFile);

            // Hutool 推荐的文件上传方式（修正版）
            HttpResponse response = HttpRequest.post(url)
                    .header("Authorization", "Bearer " + tokenSet.getPersonToken())
                    .header("Content-Type", "multipart/form-data")
                    .form("file", tempFile)
                    .timeout(60000)
                    .execute();

            tempFile.delete(); // 请求完成后删除临时文件

            Map<String, Object> resutl = new HashMap<>();
            resutl.put("text", processResponse(response));

            return AjaxClientResult.success("转换成功", resutl);
        } catch (Exception e) {
            log.error("音频转写失败，详细信息：", e);
            return AjaxClientResult.error("处理失败：" + e.getMessage());
        }
    }

    private String processResponse(HttpResponse response) {
        if (response.getStatus() != HttpStatus.HTTP_OK) {
            JSONObject error = JSONUtil.parseObj(response.body());
            throw new RuntimeException("API错误：" + error.getStr("message", "未知错误"));
        }

        //解析成对象
        JSONObject jsonObject = JSONUtil.parseObj(response.body());
        // 将 JSONObject 转换为实体对象
        CozeTranscriptionResult result = jsonObject.toBean(CozeTranscriptionResult.class);

        String text = result.getData().getText();
        return StrUtil.isBlank(text) ? "（空内容）" : text;
    }

    /**
     * 方法说明: 文字转语音
     *
     * @param
     * @return
     * @author java-101
     * @date 2025-03-23 22:16:25
     */
    @Override
    public AjaxClientResult textToSpeech(String text) {
        String url = ChatAiForCozeUtils.getApiUrlSpeech();
        // 查询系统token 使用JWT方式获取token（永久授权）
        CozeTokenSet tokenSet = cozeJwtService.getCozeTokenSetForToken();
        if (ObjectUtil.isEmpty(tokenSet)) {
            log.error("token为空，请先设置token！");
            return AjaxClientResult.error("token为空，系统错误，稍后重试");
        }
        // 直接使用文件流，避免创建临时文件
        try {

            // 构建请求体 JSON
            JSONObject requestBody = new JSONObject();
            requestBody.put("voice_id", "7426720361733144585");//音频文件使用的音色 ID
            requestBody.put("input", text);

            // 发送 POST 请求
            HttpResponse response = HttpRequest.post(url)
                    .header("Authorization", "Bearer " + tokenSet.getPersonToken())
                    .header("Content-Type", "application/json")
                    .timeout(60000 * 5)
                    .body(requestBody.toString())
                    .execute();

            // 保存音频文件到框架的图片上传目录
            String fileName = StringUtils.format("{}/{}_{}.{}", DateUtils.datePath(), FilenameUtils.getBaseName(UUID.randomUUID().toString()), Seq.getId(Seq.uploadSeqType), "mp3");
            // 上传文件路径（配置的前段）
            String localPath = ProjectConfig.getUploadPath();

            //创建文件目录及文件
            String absPath = FileUploadUtils.getAbsoluteFile(localPath, fileName).getAbsolutePath();

            Path filePath = Paths.get(absPath);
            try (java.io.OutputStream os = Files.newOutputStream(filePath)) {
                os.write(response.bodyBytes());
            } catch (IOException e) {
                e.printStackTrace();
                return AjaxClientResult.error("文件保存失败");
            }

            String urlEnd = Constants.RESOURCE_PREFIX + "/upload/" + fileName;
            Map<String, Object> result = new HashMap<>();
            result.put("audioUrl", urlEnd);

            return AjaxClientResult.success("转换成功", result);
        } catch (Exception e) {
            log.error("音频转写失败，详细信息：", e);
            return AjaxClientResult.error("处理失败：" + e.getMessage());
        }
    }

    /**
     * 获取完整的请求路径，包括：域名，端口，上下文访问路径
     *
     * @return 服务地址
     */
    public String getUrl() {
        HttpServletRequest request = ServletUtils.getRequest();
        return getDomain(request);
    }

    public static String getDomain(HttpServletRequest request) {
        StringBuffer url = request.getRequestURL();
        String contextPath = request.getServletContext().getContextPath();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).append(contextPath).toString();
    }

    /**
     * 方法说明: 对话点赞/踩
     *
     * @param
     * @return
     * @author java-101
     * @date 2025-04-10 15:43:31
     */
    @Override
    public AjaxClientResult likeOrStepOn(LikeOrStepOnDto dto) {
        if (ObjectUtil.isEmpty(dto.getChatId()) || ObjectUtil.isEmpty(dto.getLikeOrStepOn())) {
            return AjaxClientResult.error("参数错误");
        }
        if (dto.getLikeOrStepOn() != 1 && dto.getLikeOrStepOn() != 2 && dto.getLikeOrStepOn() != 3) {
            return AjaxClientResult.error("参数错误");
        }
        long count = botConversationChatMapper.selectCount(Wrappers.<BotConversationChat>lambdaQuery().eq(BotConversationChat::getChatId, dto.getChatId()));
        if (count <= 0) {
            return AjaxClientResult.error("回复不存在");
        }
        LambdaUpdateWrapper<BotConversationChat> updateWrapper = Wrappers.lambdaUpdate(BotConversationChat.class)
                .eq(BotConversationChat::getChatId, dto.getChatId())
                .set(BotConversationChat::getEvaluate, dto.getLikeOrStepOn())
                .set(BotConversationChat::getUpdateTime, DateUtils.getDate());
        int result = botConversationChatMapper.update(null, updateWrapper);
        if (result > 0) {
            return AjaxClientResult.success("成功");
        } else {
            return AjaxClientResult.error("失败");
        }
    }

    /**
     * 方法说明: 获取扣子token
     * @param
     * @return
     * @author java-101
     * @date 2025-05-10 15:13:02
     */
    @Override
    public AjaxClientResult getFeedbackToken() {

        // 获取当前请求的上下文
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes == null) {
            throw new IllegalStateException("无法获取当前HTTP请求");
        }
        // 获取原生HttpServletRequest对象
        HttpServletRequest request = attributes.getRequest();

        // 直接通过原生API获取请求头
        String authorization = request.getHeader("Authorization");
        //打印请求头
        log.info("请求头：{}", authorization);
        if(ObjectUtil.isEmpty(authorization)){
            return AjaxClientResult.error("无权限访问本接口");
        }
        if(!authorization.equals("xT5v2pA7eJ9rL0fD3gH8kM4nZ6bW2cY")){
            return AjaxClientResult.error("无权限访问本接口");
        }

        // 查询系统token 使用JWT方式获取token（永久授权）
        CozeTokenSet tokenSet = cozeJwtService.getCozeTokenSetForToken();
        if (ObjectUtil.isEmpty(tokenSet)) {
            log.error("token为空，请先设置token！");
            return AjaxClientResult.error("token为空，系统错误，稍后重试");
        }
        Map<String, Object> result = new HashMap<>();
        result.put("oauthToken", tokenSet.getPersonToken());
        return AjaxClientResult.success(result);
    }


}
