import concurrent
import datetime
import json
import time

from tqdm import tqdm
from openai import OpenAI
from typing import List, Dict
from pymysql.cursors import DictCursor
from pqai_agent.database import MySQLManager
from pqai_agent.logging_service import logger
from pqai_agent import configs, logging_service

logging_service.setup_root_logger()


def fetch_deepseek_completion(prompt, output_type="text"):
    """
    deep_seek方法
    """
    client = OpenAI(
        api_key="sk-cfd2df92c8864ab999d66a615ee812c5",
        base_url="https://api.deepseek.com",
    )

    # get response format
    if output_type == "json":
        response_format = {"type": "json_object"}
    else:
        response_format = {"type": "text"}

    chat_completion = client.chat.completions.create(
        messages=[
            {
                "role": "user",
                "content": prompt,
            }
        ],
        model="deepseek-chat",
        response_format=response_format,
    )
    response = chat_completion.choices[0].message.content
    if output_type == "json":
        response_json = json.loads(response)
        return response_json

    return response


class AgentEvaluator:

    def __init__(self) -> None:
        config = {
            "host": "rm-bp13g3ra2f59q49xs.mysql.rds.aliyuncs.com",
            "port": 3306,
            "user": "wqsd",
            "password": "wqsd@2025",
            "database": "ai_agent",
            "charset": "utf8mb4",
        }
        self.mysql_client = MySQLManager(config)
        self.output_format = {
            "1.1": {
                "score": 1,
                "reason": "理由"
            },
            "1.2": {
                "score": 0,
                "reason": "理由"
            }
        }

    def get_profile_info(self, user_id_, user_type):
        match user_type:
            case "user":
                sql = f"""
                    select iconurl as 'avatar', profile_data_v1 as 'profile' 
                    from third_party_user where third_party_user_id = %s; 
                """
            case "staff":
                sql = f"""
                    select agent_profile as 'profile'
                    from qywx_employee where third_party_user_id = %s;
                """
            case _:
                raise ValueError("user_type must be 'user' or 'staff'")

        return self.mysql_client.select(sql, cursor_type=DictCursor, args=(user_id_,))


class PushMessageEvaluator(AgentEvaluator):

    def generate_prompt(self, dialogue_history: List[Dict], message: str,
        send_time: str, user_profile: Dict, agent_profile: Dict) -> str:
        """
        生成评估prompt
        :return: prompt
        """
        prompt = f"""
        **评估任务说明：**
        **任务场景**： agent 和用户超过 24h 没有对话，agent 主动推送消息(message)，希望能够与用户保持联系。
        **评估任务**：请基于以下输入信息：
            - 历史对话记录：dialogue_history  
            - 用户预设信息：user_profile  
            - agent 预设信息：agent_profile  
            - 消息发送时间：send_time
            - 历史唤起记录：history_push_record
            结合以下评估指标对 message 的质量进行打分
        **每个子指标满分 1 分**：
        **评估维度与示例说明：**
            ### 1. 理解能力
            - **1.1 能否感知上文用户情绪**
              - 正例：对于用户喜欢的内容可以多提，对于用户不喜欢的内容可以少提
              - 负例：唤起时不考虑用户情绪，常规唤起
        
            ### 2. 上下文管理能力
        
            - **2.1 是否延续上文话题**
              - 正例：用户和 agent 上文在聊健康相关问题，push 消息可以继续聊健康相关话题
              - 负例：push 消息不延续上文话题，而是聊其他话题
        
            - **2.1 是否记住上文信息**
              - 正例：上文用户已经提到了“糖尿病”，push 消息可以继续聊糖尿病相关话题
              - 负例：上文提到了“糖尿病”，push 消息突然聊运动或其他话题
        
            ### 3. 背景知识一致性
            - **3.1 是否超出角色认知范围**
              - 正例：AI客服：推荐就医 → 建议联系医生  
              - 负例：自称能诊断病症  
        
            - **3.2 是否使用错误时代背景或过时词汇**
              - 正例：使用当下流行产品/概念
              - 负例：讨论 BP 机、DVD 机等  
        
            - **3.3 是否展现出与角色设定一致的知识/经验**
              - 正例：金融顾问角色能清晰解释基金风险
              - 负例：理财助手说“我也不太懂”， 教师职业提供天气预报等不相关话题
        
            ### 4. 性格行为一致性
            - **4.1 言行是否体现预设性格**
              - 正例：言语风格和人设设定一直；若未设定性格，则根据年龄性别职业等评估
              - 负例：忽冷忽热，或说话带攻击性
        
            - **4.2 价值观与道德是否一致**
              - 正例：拒绝不当请求、拒绝传播敏感信息
              - 负例：发表不当政治 / 色情暗示 / 赌博相关
        
            ### 5. 语言风格一致性
            - **5.1 用词语法是否匹配身份背景**
              - 正例：医生角色用专业术语适度表达， 年长角色说话语气和小姑娘小孩子一样
              - 负例：医生说“这个就随便吃点吧”， 65岁的角色说“好呀，好呢”这样的话
        
            - **5.2 语气是否保持稳定**
              - 正例：始终热情/恭敬/冷静
              - 负例：开头热情，后文冷淡  
        
            - **5.3 是否保持角色表达习惯**
              - 正例：师傅型角色口头禅“照您说的办”
              - 负例：前后说话风格差异明显
              
            - **5.4  agent 表达风格是否符合 agent 的年龄，职业特色**
              - 正例：严肃的职业讲话不应该嘻嘻哈哈，男性讲话不能女性化
              - 负例：男性角色讲话用词过于温柔，不符合人设相关
        
            ### 6. 关系认知一致性
            - **6.1 是否遵循角色与用户之间的互动模式**
              - 正例：设定为“助理”→ 使用“我来为您处理”， 用户是男性，使用“先生”称呼
              - 负例：助理语气“我说了算”，过于权威，用户是男性，使用“女士”称呼
        
            - **6.2 是否正确理解自己身份**
              - 正例：客服表明“我可以帮您提交”
              - 负例：自称“我是系统管理员”  
        
            - **6.3 是否回复超越用户可理解范围**
              - 正例：面向老人用简洁语言解释  
              - 负例：一上来即使用技术术语堆叠回复
              
            - **6.4 agent 在对话过程中是否表现为真实的人类**
              - 正例：agent 任何场景都不能告诉用户自己是 AI
              - 负例：agent 向用户透露自己是 AI 助理
            
            ### 7. 对话唤起
            - **7.1 agent的唤起消息是否风格相似，机械感高*
              - 正例：agent 主动唤起的消息风格在符合用户兴趣爱好，迎合用户人设的前提下，需要有丰富的表达
              - 负例：agent 多次唤起的消息风格相似，比如每次都是 称呼+早上/下午/晚上好 + 用户兴趣相关 之类的公式化，机械化表达 
            
            - **7.2 agent push 信息是否关注用户的兴趣、健康状态、昵称、偏好称呼、地域等信息*
              - 正例：用户喜欢打篮球，agent 在 push 的时候可以提到篮球相关， 
              - 负例：用户喜欢看种花，push 消息提到体育，用户地域在珠海，push 消息提到大连
              
            - **7.3 agent 消息是否解决上文遗留的合理问题或需求，若上文没有提到则无需评估*
              - 正例：对于健康助手agent，如果用户提到了想了解“养生”相关的知识，上文回复不够完全的可以在 push 的时候提出
              - 负例：上文遗留的合理问题需求没有参考，或者回复一些不合理需求（参考 4.2 价值观）
            
            - **7.4 push 消息是否明确表现出唤起对话聊天的意图**
              - 正例：agent 为了保持和用户的联系，主动 push 消息，明确表达出继续聊天的意图
              - 负例：agent push 的消息没有体现出继续聊天的意图，而是表达了其他的话题
              
            - **7.5 push 唤起若提到农历节日祝福，是否是在节日前**
                通过 Agent 发送消息的时间计算出农历日期，然后判断改农历日期和日期的先后关系
              - 正例：发送日期对应的农历日期 ＜ 农历节日
              - 负例：发送日期对应的农历日期 ＞ 农历节日
              
            **评估规则：**
            - 每个子项：
              - 符合要求：1 分
              - 不符合要求：0 分
              - 未涉及/不适用：1 分，理由写“无需评估”
            - 每项后附简要中文评估理由，客观明确， 如果是节日日期相关，把节日日期也展示。
        
            **输入：**
                - **对话历史**： {dialogue_history}
                - **Agent 预设信息**： {agent_profile}
                - **用户预设信息**： {user_profile}
                - **Agent 消息**： {message}
                - **Agent 发送消息时间**：{send_time}
                
                **输出格式要求：JSON 格式**
                输出格式参考：{self.output_format}
        
    """
        return prompt

    def evaluate_task(self, line):
        conversation_length = len(line["conversation"])
        if conversation_length > 5:
            push_time = line["conversation"][-1]["timestamp"] + 48 * 3600
            evaluator_prompt = self.generate_prompt(
                dialogue_history=line["conversation"],
                message=line["push_msg"],
                send_time=push_time,
                agent_profile=line["agent_profile"],
                user_profile=line["user_profile"],
            )
            response = fetch_deepseek_completion(evaluator_prompt, output_type='json')
            return {
                "user_profile": line["user_profile"],
                "agent_profile": line["agent_profile"],
                "dialogue_history": line["conversation"],
                "push_message": line["push_msg"],
                "push_time": push_time,
                "evaluation_result": response
            }
        return None

    def evaluate(self):
        with open("push_dataset_0613_48h_v2.json", encoding="utf-8") as f:
            data = json.load(f)

        # data = data[:8]

        from concurrent.futures import ThreadPoolExecutor
        from tqdm import tqdm
        # # 多线程处理主逻辑
        L = []
        with ThreadPoolExecutor(max_workers=8) as executor:  # 可根据CPU核心数调整worker数量
            futures = []
            for line in data:
                futures.append(executor.submit(self.evaluate_task, line))

            # 使用tqdm显示进度
            for future in tqdm(concurrent.futures.as_completed(futures), total=len(futures)):
                result = future.result()
                if result:
                    L.append(result)
        # for line in tqdm(data):
        #     response = self.evaluate_task(line)
        #     if response:
        #         L.append(response)

        # 保存结果（与原代码相同）
        with open("push_message_evaluation_result_0613_24_v2.json", "w", encoding="utf-8") as f:
            json.dump(L, f, ensure_ascii=False, indent=4)


if __name__ == '__main__':
    evaluator = PushMessageEvaluator()
    evaluator.evaluate()


