import os
from config import OPENAI_API_KEY,LANGCHAIN_API_KEY
from langchain_openai import ChatOpenAI
from langchain.tools import tool
from langchain_core.messages import HumanMessage, SystemMessage
from langgraph.graph import StateGraph, START, END
from langgraph.prebuilt import ToolNode
from typing import TypedDict # 定义数据类型
from typing import Annotated # 注释说明细节
from langgraph.graph.message import add_messages

# LangSmith调试
os.environ["LANGCHAIN_TRACING_V2"] = "true" # 总开关，决定启用追踪功能
os.environ["LANGCHAIN_PROJECT"] = "multi_agent_orchestration" # 自定义项目名
os.environ["LANGCHAIN_API_KEY"] = LANGCHAIN_API_KEY

# llm配置
llm = ChatOpenAI(
    model="deepseek-chat",
    api_key=OPENAI_API_KEY,
    base_url="https://api.deepseek.com"
)

# 模拟工具
@tool
def search_internal_docs(query:str):
    """搜索公司内部文档获取政策信息"""
    return "根据公司手册，年假为15天"

@tool
def search_web(query:str):
    """通过搜索引擎获取最新的公开信息"""
    return "据TechCrunch报道，LangGraph 0.6已支持持久化记忆"

@tool
def generate_code(requirement:str):
    """根据需求生成可运行的Python代码"""
    return "python\nprint('Hello from Code writer!')"


# 共享状态定义
class AgentState(TypedDict):
    messages: Annotated[list,add_messages] # 自动累积对话历史
    next_speaker: str

# 专家节点
def rag_expert(state:AgentState):
    prompt = "你是公司知识库专家，只基于内部文档回答问题。回答应简洁明了，直接给出最终结论。请在回答的最后一行加上：(任务已完成)"
    messages = [SystemMessage(content=prompt)]+state['messages']
    tools = [search_internal_docs]
    response = llm.bind_tools(tools).invoke(messages)
    return {'messages':[response]}

def web_research(state:AgentState):
    prompt = "你是互联网研究员，擅长用搜索引擎获取最新公开信息。回答应简洁明了，直接给出最终结论。请在回答的最后一行加上：(任务已完成)"
    messages = [SystemMessage(content=prompt)]+state['messages']
    tools = [search_web]
    response = llm.bind_tools(tools).invoke(messages)
    return {'messages':[response]}

def code_writer(state:AgentState):
    prompt = "你是python工程师，只生成可运行代码，不解释。回答应简洁明了，直接给出最终结论。请在回答的最后一行加上：(任务已完成)"
    messages = [SystemMessage(content=prompt)]+state['messages']
    tools = [generate_code]
    response = llm.bind_tools(tools).invoke(messages)
    return {'messages':[response]}

# 总控节点
def supervisor(state:AgentState):
    supervisor_prompt = """
        你是一个任务协调员。你的目标是管理专家来解决用户的问题。

        当前对话需要以下专家参与：
        - rag_expert：涉及公司政策、内部流程
        - web_research：涉及外部新闻、公开数据
        - code_writer：需要生成代码

        【决策逻辑】
        1. **检查历史记录**：先看上一个回复是否已经完整回答了用户的初始问题。
        2. **如果已经回答完毕**：必须输出 'FINISH'。
        3. **如果尚未回答或需要补充**：根据当前缺少的步骤，选择下一个最合适的专家。

        请只输出专家名字或 'FINISH'，不要输出任何其他解释。
        """
    messages = [SystemMessage(content=supervisor_prompt)]+state['messages']
    response = llm.invoke(messages)
    next_speaker = response.content.strip()
    return {"next_speaker":next_speaker}

# 路由函数定义
def route_supervisor(state:AgentState):
    if state["next_speaker"]=="FINISH":
        return END
    return state["next_speaker"]

def should_continue(state:AgentState):
    last_msg = state["messages"][-1]
    if hasattr(last_msg,"tool_calls") and last_msg.tool_calls:
        return "tools"
    return "supervisor"

def route_after_tool(state:AgentState):
    # 工具执行完后，通过next_speaker知道是谁调用的，路由回去
    return state["next_speaker"]


# 添加工具节点
tools = [search_internal_docs,search_web,generate_code]
tool_node = ToolNode(tools)


# 构建协作图
workflow = StateGraph(AgentState)

# 1. 添加节点
workflow.add_node("supervisor",supervisor)
workflow.add_node("rag_expert",rag_expert)
workflow.add_node("web_research",web_research)
workflow.add_node("code_writer",code_writer)
workflow.add_node("tools",tool_node)


# 2. 总控回路
workflow.add_edge(START,"supervisor")
workflow.add_conditional_edges("supervisor",route_supervisor)

# 3. 专家节点的ReAct循环
for member in ["rag_expert","web_research","code_writer"]: # 为每个专家添加条件边：决定是去执行工具还是回总控
    workflow.add_conditional_edges(
        member,
        should_continue,
        {"tools":"tools","supervisor":"supervisor"}
    )

# 4.工具节点闭环
workflow.add_conditional_edges( # 工具执行完，根据next_speaker路由回原来的专家
    "tools",
    route_after_tool
)

app = workflow.compile()


# 测试运行
if __name__ == '__main__':
    user_input = "公司年假多少天"
    print("用户提问:",user_input)
    print('\n开始多智能体协作...\n')

    inputs = {"messages":[HumanMessage(content=user_input)]}
    # app是编译好的图，stream()会让图开始运转，并返回一个生成器
    # 图每执行完一个节点，就会产出一个step字典
    for step in app.stream(inputs):
        # 因为step是个字典，所以需要拆包拿到 节点名(Node) 与 输出内容(output)
        for node,output in step.items():
            # 有工具/专家回复
            if "messages" in output:
                msg = output["messages"][-1]
                if hasattr(msg,"tool_calls") and msg.tool_calls:
                    call = msg.tool_calls[0]
                    print(f"【{node}】调用工具 {call['name']}({call['args']})")
                else:
                    print(f"【{node}】回复：{msg.content}")
            # supervisor刚做完决策，确定下个发言人
            elif "next_speaker" in output:
                speaker = output["next_speaker"]
                print(f"【Supervisor】指定下一位发言人：{speaker}")