#!/home/ubuntu/miniconda3/envs/act-env/bin/python
import re
import os
import rclpy
from rclpy.node import Node
from std_msgs.msg import String, Bool
from openai import OpenAI
from pypinyin import lazy_pinyin
from difflib import SequenceMatcher

def text_to_pinyin(text):
    """将中文文本转换为拼音"""
    return ''.join(lazy_pinyin(text))

def pinyin_similarity(text1, text2):
    """计算两个文本的拼音相似度"""
    pinyin1 = text_to_pinyin(text1)
    pinyin2 = text_to_pinyin(text2)
    return SequenceMatcher(None, pinyin1, pinyin2).ratio()

def fuzzy_match_keywords(text, keywords, threshold=0.7):
    """模糊匹配关键词，返回匹配到的关键词或None
    
    首先尝试直接匹配，然后尝试拼音模糊匹配
    """
    # 1. 首先尝试直接字符串匹配（最准确）
    for keyword in keywords:
        if keyword in text:
            return keyword
    
    # 2. 如果直接匹配失败，尝试拼音模糊匹配
    text_pinyin = text_to_pinyin(text)
    for keyword in keywords:
        keyword_pinyin = text_to_pinyin(keyword)
        # 检查关键词的拼音是否是文本拼音的子串
        if keyword_pinyin in text_pinyin:
            return keyword
        
        # 计算相似度（作为备选方案）
        similarity = pinyin_similarity(text, keyword)
        if similarity >= threshold:
            return keyword
    
    return None

class VoiceLLMNode(Node):
    def __init__(self):
        super().__init__('voice_llm_node')
        
        # 初始化 OpenAI 客户端（连接到 xInference 服务）
        self.client = OpenAI(
            base_url="http://183.252.196.135:7871/v1",
            api_key="cannot_empty",  # xInference 不需要实际的 API key
            
        )
        self.model_name = "Qwen3.5-35B-int4"
        self.get_logger().info(f'成功初始化 OpenAI 客户端，模型: {self.model_name}')
        
        # 创建订阅者，接收语音识别结果
        self.subscription = self.create_subscription(
            String,
            'voice_words',#订阅语音识别结果话题
            self.listener_callback,
            10)
        
        # 创建发布者，发布 LLM 回复
        self.publisher = self.create_publisher(String, 'llm_reply', 10)
        
        # 创建激活状态发布者
        self.activation_publisher = self.create_publisher(Bool, 'llm_activation', 10)
        
        # 创建TTS播放状态订阅者
        self.tts_status_subscription = self.create_subscription(
            Bool,
            'tts_playback_status',
            self.tts_status_callback,
            10)
        
        # 订阅人脸信息话题
        self.face_subscription = self.create_subscription(
            String,
            'user_face',
            self.face_callback,
            10)
        
        # 发布人脸注册请求话题
        self.face_registration_publisher = self.create_publisher(
            String,
            'load_face',
            10)
        
        # 激活状态，用于跟踪是否已唤醒
        self.is_activated = False
        
        # TTS播放状态，用于抑制voice_words接收
        self.is_tts_playing = False
        
        # 人脸信息缓存
        self.last_face_info = None
        
        self.get_logger().info('Voice LLM Node 已启动，正在监听 /voice_words...')

    def tts_status_callback(self, msg):
        """更新TTS播放状态"""
        self.is_tts_playing = msg.data
        self.get_logger().info(f'TTS播放状态已更新: {"播放中" if self.is_tts_playing else "空闲"}')
    
    def face_callback(self, msg):
        """处理人脸信息回调"""
        try:
            face_info = msg.data
            self.get_logger().info(f'收到人脸信息: {face_info}')
            # 缓存人脸信息
            self.last_face_info = face_info
        except Exception as e:
            self.get_logger().error(f'处理人脸信息时出错: {str(e)}')

    def publish_activation_state(self):
        """发布激活状态"""
        msg = Bool()
        msg.data = self.is_activated
        self.activation_publisher.publish(msg)
        self.get_logger().info(f'发布激活状态: {self.is_activated}')

    def listener_callback(self, msg):
        # 如果TTS正在播放，抑制接收voice_words
        if self.is_tts_playing:
            self.get_logger().info('TTS正在播放，抑制接收voice_words')
            return
        
        raw_text = msg.data
        self.get_logger().info(f'收到语音信息: "{raw_text}"')

        # 1. 解析信息，去除时间戳前缀 [YYYY-MM-DD HH:MM:SS.mmm]
        # 使用正则表达式匹配并去除括号内的内容
        text_content = re.sub(r'^\[.*?\]\s*', '', raw_text)
        self.get_logger().info(f'去除时间戳后: "{text_content}"')
        
        if not text_content:
            self.get_logger().info('文本内容为空，忽略')
            return

        # 2. 检查是否包含唤醒词"小优"（支持拼音模糊匹配）
        wake_word = fuzzy_match_keywords(text_content, ['小优'], threshold=0.6)
        self.get_logger().info(f'唤醒词检测结果: wake_word="{wake_word}", is_activated={self.is_activated}')
        
        if wake_word:
            self.is_activated = True
            self.publish_activation_state()
            self.get_logger().info(f'检测到唤醒词"{wake_word}"（拼音模糊匹配），进入持续交互模式')
            # 可选：去掉唤醒词后再发送给大模型，或者直接保留也可以
            # 这里选择保留以让大模型知道自身被呼唤
            self.get_logger().info(f'正在请求 LLM，发送内容: "{text_content}"')
            self.process_llm_request(text_content)
            return

        # 3. 如果未激活，忽略消息
        if not self.is_activated:
            self.get_logger().info('未激活，已忽略。请说"小优"来唤醒。')
            return

        # 4. 检查退出指令（支持拼音模糊匹配）
        exit_keywords = ['再见', '退出', '结束']
        exit_word = fuzzy_match_keywords(text_content, exit_keywords, threshold=0.6)
        if exit_word:
            self.is_activated = False
            self.publish_activation_state()
            self.get_logger().info(f'检测到退出指令"{exit_word}"（拼音模糊匹配），退出持续交互模式')
            reply_msg = String()
            reply_msg.data = '好的，再见！'
            self.publisher.publish(reply_msg)
            return

        # 5. 检查是否需要记住人脸
        remember_pattern = r'请记住我，我叫(.*)' 
        match = re.search(remember_pattern, text_content)
        if match:
            name = match.group(1).strip()
            if name:
                self.get_logger().info(f'用户请求记住人脸，姓名: {name}')
                # 发布人脸注册请求
                msg = String()
                msg.data = name
                self.face_registration_publisher.publish(msg)
                # 回复用户
                reply_msg = String()
                reply_msg.data = f'好的，我会记住你，{name}！'
                self.publisher.publish(reply_msg)
                return
        
        # 6. 已激活，处理消息
        self.get_logger().info(f'正在请求 LLM，发送内容: "{text_content}"')
        self.process_llm_request(text_content)

    def process_llm_request(self, text_content):
        try:
            # 构造消息发送给大模型
            system_prompt = (
                '你是智能服务机器人"小优"。你可以执行以下动作：起立、向前转、向左转、向右走、向左走、向右走、坐下。'
                '当用户要求你执行上述动作时，不要回复任何内容。'
                '如果用户问其他问题，请作为一个酒吧服务机器人，简明扼要、有礼貌地回答。'
                "说话尽量友好风趣，每次输出最多20个字"
                '\n\n最近的人脸信息：'
                f'{self.last_face_info if self.last_face_info else "无"}'
            )
            messages = [
                {'role': 'system', 'content': system_prompt},
                {'role': 'user', 'content': text_content},
                
            ]
            
            # 调用 OpenAI API (xInference 兼容 OpenAI 接口)
            response = self.client.chat.completions.create(
                model=self.model_name,
                messages=messages,
                max_tokens=1024*10,
                reasoning_effort="none",
                temperature=0.5
            )

            # 打印完整的响应对象
            self.get_logger().info(f'完整的 LLM 响应: {response}')

            # 处理并发布回复
            reply_content = response.choices[0].message.content
            self.get_logger().info(f'LLM 回复: "{reply_content}"')
            
            reply_msg = String()
            reply_msg.data = reply_content
            self.publisher.publish(reply_msg)

        except Exception as e:
            self.get_logger().error(f'处理过程中发生错误: {str(e)}')
            # 发送错误消息
            reply_msg = String()
            reply_msg.data = '处理请求时发生错误，请稍后再试'
            self.publisher.publish(reply_msg)

def main(args=None):
    rclpy.init(args=args)
    node = VoiceLLMNode()
    try:
        rclpy.spin(node)
    except KeyboardInterrupt:
        pass
    finally:
        node.destroy_node()
        rclpy.shutdown()

if __name__ == '__main__':
    main()
