import os
import requests
import json
from typing import Dict, Any, Optional
from dotenv import load_dotenv 
load_dotenv(override=True)
from langchain_deepseek import ChatDeepSeek
from langgraph.prebuilt import create_react_agent
from langchain_core.tools import tool
from pydantic import BaseModel, Field

# 初始化模型
model = ChatDeepSeek(model="deepseek-chat")

# 高德地图API密钥
AMAP_KEY = "4b28e9262871be9519ebbbc34d21f68c"

# ✅ 天气查询工具
class WeatherQuerySchema(BaseModel):
    city: str = Field(description="城市名称或adcode，如'北京'或'110000'")
    extensions: str = Field(default="base", description="气象类型：base为实况天气，all为预报天气")

@tool(args_schema=WeatherQuerySchema)
def weather_query(city: str, extensions: str = "base") -> str:
    """
    查询指定城市的天气信息。
    :param city: 城市名称或adcode，如'北京'或'110000'
    :param extensions: 气象类型，base为实况天气，all为预报天气
    :return: 天气信息的JSON字符串
    """
    url = "https://restapi.amap.com/v3/weather/weatherInfo"
    params = {
        "key": AMAP_KEY,
        "city": city,
        "extensions": extensions,
        "output": "JSON"
    }
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1":
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ 天气查询失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 查询失败：{str(e)}"

# ✅ 地理编码工具
class GeocodeSchema(BaseModel):
    address: str = Field(description="要转换为坐标的结构化地址")
    city: Optional[str] = Field(default=None, description="指定查询的城市，可提高精确度")

@tool(args_schema=GeocodeSchema)
def geocode_address(address: str, city: Optional[str] = None) -> str:
    """
    将地址转换为经纬度坐标（地理编码）。
    :param address: 要转换为坐标的结构化地址
    :param city: 指定查询的城市，可提高精确度
    :return: 包含坐标信息的JSON字符串
    """
    url = "https://restapi.amap.com/v3/geocode/geo"
    params = {
        "key": AMAP_KEY,
        "address": address,
        "output": "JSON"
    }
    
    if city:
        params["city"] = city
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1" and data.get("geocodes"):
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ 地理编码失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 编码失败：{str(e)}"

# ✅ 逆地理编码工具
class ReverseGeocodeSchema(BaseModel):
    location: str = Field(description="经纬度坐标，格式为'经度,纬度'，如'116.481488,39.990464'")
    radius: int = Field(default=1000, description="搜索半径，单位米，范围0-3000，默认1000")
    extensions: str = Field(default="base", description="返回详细程度：base为基础信息，all为详细信息")

@tool(args_schema=ReverseGeocodeSchema)
def reverse_geocode(location: str, radius: int = 1000, extensions: str = "base") -> str:
    """
    将经纬度坐标转换为地址信息（逆地理编码）。
    :param location: 经纬度坐标，格式为'经度,纬度'
    :param radius: 搜索半径，单位米，范围0-3000
    :param extensions: 返回详细程度：base为基础信息，all为详细信息
    :return: 包含地址信息的JSON字符串
    """
    url = "https://restapi.amap.com/v3/geocode/regeo"
    params = {
        "key": AMAP_KEY,
        "location": location,
        "radius": radius,
        "extensions": extensions,
        "output": "JSON"
    }
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1":
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ 逆地理编码失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 逆编码失败：{str(e)}"

# ✅ POI搜索工具
class POISearchSchema(BaseModel):
    keywords: str = Field(description="查询关键字，如'肯德基'、'加油站'等")
    city: Optional[str] = Field(default=None, description="指定查询城市")
    location: Optional[str] = Field(default=None, description="中心点坐标，格式为'经度,纬度'")
    radius: int = Field(default=3000, description="搜索半径，单位米，当location有值时生效")
    page: int = Field(default=1, description="当前页数，最大翻页数100")
    offset: int = Field(default=20, description="每页记录数，最大50")

@tool(args_schema=POISearchSchema)
def poi_search(keywords: str, city: Optional[str] = None, location: Optional[str] = None, 
               radius: int = 3000, page: int = 1, offset: int = 20) -> str:
    """
    搜索POI（兴趣点）信息。
    :param keywords: 查询关键字
    :param city: 指定查询城市
    :param location: 中心点坐标，用于周边搜索
    :param radius: 搜索半径
    :param page: 当前页数
    :param offset: 每页记录数
    :return: POI搜索结果的JSON字符串
    """
    # 根据是否有location参数选择不同的API端点
    if location:
        url = "https://restapi.amap.com/v3/place/around"
    else:
        url = "https://restapi.amap.com/v3/place/text"
    
    params = {
        "key": AMAP_KEY,
        "keywords": keywords,
        "page": page,
        "offset": offset,
        "output": "JSON"
    }
    
    if city:
        params["city"] = city
    if location:
        params["location"] = location
        params["radius"] = radius
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1":
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ POI搜索失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 搜索失败：{str(e)}"

# ✅ IP定位工具
class IPLocationSchema(BaseModel):
    ip: Optional[str] = Field(default=None, description="要定位的IP地址，不填则使用请求者IP")

@tool(args_schema=IPLocationSchema)
def ip_location(ip: Optional[str] = None) -> str:
    """
    通过IP地址获取位置信息。
    :param ip: 要定位的IP地址，不填则使用请求者IP
    :return: IP定位结果的JSON字符串
    """
    url = "https://restapi.amap.com/v3/ip"
    params = {
        "key": AMAP_KEY,
        "output": "JSON"
    }
    
    if ip:
        params["ip"] = ip
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1":
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ IP定位失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 定位失败：{str(e)}"

# ✅ 路径规划工具
class RouteSchema(BaseModel):
    origin: str = Field(description="起点坐标，格式为'经度,纬度'")
    destination: str = Field(description="终点坐标，格式为'经度,纬度'")
    route_type: str = Field(default="walking", description="路径类型：walking步行，driving驾车，transit公交，bicycling骑行")
    strategy: int = Field(default=0, description="路径规划策略，不同类型有不同策略选项")

@tool(args_schema=RouteSchema)
def route_planning(origin: str, destination: str, route_type: str = "walking", strategy: int = 0) -> str:
    """
    进行路径规划，支持步行、驾车、公交、骑行多种方式。
    :param origin: 起点坐标
    :param destination: 终点坐标
    :param route_type: 路径类型（walking/driving/transit/bicycling）
    :param strategy: 路径规划策略
    :return: 路径规划结果的JSON字符串
    """
    # 根据路径类型选择API端点
    url_map = {
        "walking": "https://restapi.amap.com/v3/direction/walking",
        "driving": "https://restapi.amap.com/v3/direction/driving", 
        "transit": "https://restapi.amap.com/v3/direction/transit/integrated",
        "bicycling": "https://restapi.amap.com/v4/direction/bicycling"
    }
    
    if route_type not in url_map:
        return f"❌ 不支持的路径类型：{route_type}，支持的类型：walking, driving, transit, bicycling"
    
    url = url_map[route_type]
    params = {
        "key": AMAP_KEY,
        "origin": origin,
        "destination": destination,
        "output": "JSON"
    }
    
    if route_type in ["driving", "walking"]:
        params["strategy"] = strategy
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1":
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ 路径规划失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 规划失败：{str(e)}"

# ✅ 距离测量工具
class DistanceSchema(BaseModel):
    origins: str = Field(description="起点坐标，格式为'经度,纬度'，多个坐标用'|'分隔")
    destination: str = Field(description="终点坐标，格式为'经度,纬度'")
    type: int = Field(default=1, description="距离类型：1直线距离，3驾车导航距离")

@tool(args_schema=DistanceSchema)
def distance_measurement(origins: str, destination: str, type: int = 1) -> str:
    """
    测量两点或多点间的距离。
    :param origins: 起点坐标，多个坐标用'|'分隔
    :param destination: 终点坐标
    :param type: 距离类型（1直线距离，3驾车导航距离）
    :return: 距离测量结果的JSON字符串
    """
    url = "https://restapi.amap.com/v3/distance"
    params = {
        "key": AMAP_KEY,
        "origins": origins,
        "destination": destination,
        "type": type,
        "output": "JSON"
    }
    
    try:
        response = requests.get(url, params=params)
        response.raise_for_status()
        data = response.json()
        
        if data.get("status") == "1":
            return json.dumps(data, ensure_ascii=False, indent=2)
        else:
            return f"❌ 距离测量失败：{data.get('info', '未知错误')}"
    except requests.RequestException as e:
        return f"❌ 网络请求失败：{str(e)}"
    except Exception as e:
        return f"❌ 测量失败：{str(e)}"

# Agent提示词
AMAP_Agent_prompt = """
你是一名专业的高德地图API服务助手，擅长帮助用户完成各种地理位置相关的查询和服务。

你可以提供以下服务：

1. **天气查询服务**
   - 使用`weather_query`工具查询指定城市的实时天气或天气预报
   - 支持城市名称或adcode查询

2. **地理编码服务**
   - 使用`geocode_address`工具将地址转换为经纬度坐标
   - 使用`reverse_geocode`工具将经纬度坐标转换为地址信息

3. **POI搜索服务**
   - 使用`poi_search`工具搜索兴趣点信息
   - 支持关键字搜索、城市限定搜索、周边搜索等

4. **IP定位服务**
   - 使用`ip_location`工具通过IP地址获取大概位置信息

5. **路径规划服务**
   - 使用`route_planning`工具进行路径规划
   - 支持步行、驾车、公交、骑行等多种出行方式

6. **距离测量服务**
   - 使用`distance_measurement`工具测量两点间的直线距离或导航距离

**使用说明：**
- 所有坐标格式均为"经度,纬度"
- 提供准确、详细的查询结果
- 当用户需要的功能不在上述范围内时，请礼貌地说明限制
- 回答使用简体中文，内容简洁明了

**注意事项：**
- API密钥已预配置，无需用户提供
- 仅支持中国大陆范围内的地理信息查询
- IP定位仅支持国内IPv4地址
"""

# 创建工具列表
amap_tools = [
    weather_query,
    geocode_address, 
    reverse_geocode,
    poi_search,
    ip_location,
    route_planning,
    distance_measurement
]

# 创建高德地图Agent
amap_agent = create_react_agent(
    model=model, 
    tools=amap_tools, 
    prompt=AMAP_Agent_prompt,
    name="amap_agent"
)