import tkinter as tk
from tkinter import ttk, messagebox
import threading
import requests


class WeatherApp:
    def __init__(self, root):
        self.root = root
        self.root.title("🌤 天气查询小工具")
        self.root.geometry("520x480")
        self.root.resizable(False, False)
        self.root.configure(bg="#f0f4f8")

        self.build_ui()

    def build_ui(self):
        # 标题
        title = tk.Label(self.root, text="🌤 天气查询",
                         font=("Microsoft YaHei", 18, "bold"),
                         fg="#1565c0", bg="#f0f4f8")
        title.pack(pady=12)

        # 输入区
        input_frame = tk.Frame(self.root, bg="#f0f4f8")
        input_frame.pack(pady=5)

        tk.Label(input_frame, text="城市名：", font=("Microsoft YaHei", 11),
                 bg="#f0f4f8").grid(row=0, column=0, padx=5)

        self.city_entry = tk.Entry(input_frame, width=18,
                                   font=("Microsoft YaHei", 11))
        self.city_entry.grid(row=0, column=1, padx=5)
        self.city_entry.insert(0, "北京")
        self.city_entry.bind("<Return>", lambda e: self.start_query())

        self.query_btn = tk.Button(input_frame, text="🔍 查询",
                                   command=self.start_query,
                                   bg="#1976d2", fg="white",
                                   font=("Microsoft YaHei", 10, "bold"),
                                   width=8, relief="flat", cursor="hand2")
        self.query_btn.grid(row=0, column=2, padx=8)

        # 状态提示
        self.status_label = tk.Label(self.root, text="输入城市名后点击查询",
                                     font=("Microsoft YaHei", 9),
                                     fg="#777", bg="#f0f4f8")
        self.status_label.pack(pady=3)

        # 结果展示区
        result_frame = tk.Frame(self.root, bg="#f0f4f8")
        result_frame.pack(fill="both", expand=True, padx=20, pady=8)

        self.result_text = tk.Text(result_frame, height=14, width=56,
                                   font=("Microsoft YaHei", 11),
                                   bg="white", fg="#333",
                                   relief="groove", bd=2,
                                   wrap="word", state="disabled")
        self.result_text.pack(fill="both", expand=True)

        # 底部说明
        tip = tk.Label(self.root, text="数据来源：Open-Meteo（免费，无需 API Key）",
                       font=("Microsoft YaHei", 8), fg="#999", bg="#f0f4f8")
        tip.pack(side="bottom", pady=5)

    # ---------- 查询逻辑 ----------
    def start_query(self):
        city = self.city_entry.get().strip()
        if not city:
            messagebox.showwarning("提示", "请输入城市名！")
            return

        # 禁用按钮，防止重复点击
        self.query_btn.config(state="disabled", text="查询中…")
        self.status_label.config(text=f"正在查询「{city}」…", fg="#1976d2")

        # 后台线程执行网络请求，避免阻塞 GUI
        threading.Thread(target=self.query_weather,
                         args=(city,), daemon=True).start()

    def query_weather(self, city):
        try:
            # 1. 地理编码：城市名 → 经纬度
            geo_url = "https://geocoding-api.open-meteo.com/v1/search"
            geo_params = {"name": city, "count": 1, "language": "zh"}
            geo_resp = requests.get(geo_url, params=geo_params, timeout=8)
            geo_resp.raise_for_status()
            geo_data = geo_resp.json()

            if "results" not in geo_data or not geo_data["results"]:
                self.finish_query("❌ 未找到该城市，请检查名称是否正确。", is_error=True)
                return

            loc = geo_data["results"][0]
            lat, lon = loc["latitude"], loc["longitude"]
            display_name = loc.get("name", city)

            # 2. 查询当前天气
            weather_url = "https://api.open-meteo.com/v1/forecast"
            weather_params = {
                "latitude": lat,
                "longitude": lon,
                "current_weather": "true",
                "timezone": "auto",
            }
            w_resp = requests.get(weather_url, params=weather_params, timeout=8)
            w_resp.raise_for_status()
            w_data = w_resp.json()

            cw = w_data.get("current_weather")
            if not cw:
                self.finish_query("⚠️ 未能获取到天气数据，请稍后重试。", is_error=True)
                return

            # 3. 整理结果
            temp = cw.get("temperature", "暂未获取")
            wind = cw.get("windspeed", "暂未获取")
            wind_dir = cw.get("winddirection", "")
            time_str = cw.get("time", "")
            weather_code = cw.get("weathercode", -1)

            desc = self.weather_code_to_text(weather_code)

            result = (
                f"📍 城市：{display_name}\n"
                f"🕒 更新时间：{time_str}\n"
                f"{'─' * 36}\n"
                f"🌡  温度：{temp} °C\n"
                f"☁️  天气：{desc}\n"
                f"💨 风速：{wind} km/h\n"
                f"🧭 风向：{wind_dir}°\n"
                f"{'─' * 36}\n"
                f"📌 数据来源：Open-Meteo 免费接口"
            )
            self.finish_query(result, is_error=False)

        except requests.exceptions.Timeout:
            self.finish_query("⏰ 请求超时，请检查网络后重试。", is_error=True)
        except requests.exceptions.ConnectionError:
            self.finish_query("🌐 网络连接失败，请检查网络。", is_error=True)
        except requests.exceptions.RequestException as e:
            self.finish_query(f"⚠️ 请求异常：{e}", is_error=True)
        except (KeyError, ValueError) as e:
            self.finish_query(f"⚠️ 数据解析异常：{e}", is_error=True)
        except Exception as e:
            self.finish_query(f"⚠️ 未知错误：{e}", is_error=True)

    def finish_query(self, text, is_error=False):
        """在主线程中更新界面（线程安全）"""
        def update():
            self.result_text.config(state="normal")
            self.result_text.delete("1.0", tk.END)
            self.result_text.insert("1.0", text)

            if is_error:
                self.result_text.config(fg="#c62828")
                self.status_label.config(text="查询失败", fg="#c62828")
            else:
                self.result_text.config(fg="#2e7d32")
                self.status_label.config(text="查询完成 ✓", fg="#2e7d32")

            self.result_text.config(state="disabled")
            self.query_btn.config(state="normal", text="🔍 查询")

        self.root.after(0, update)

    @staticmethod
    def weather_code_to_text(code):
        """将 Open-Meteo 天气代码转为中文描述"""
        mapping = {
            0: "晴朗 ☀️",
            1: "大部晴朗 🌤",
            2: "局部多云 ⛅",
            3: "阴天 ☁️",
            45: "有雾 🌫",
            48: "雾凇 🌫",
            51: "小毛毛雨 🌦",
            53: "中毛毛雨 🌦",
            55: "大毛毛雨 🌧",
            61: "小雨 🌧",
            63: "中雨 🌧",
            65: "大雨 🌧",
            71: "小雪 🌨",
            73: "中雪 🌨",
            75: "大雪 ❄️",
            80: "小阵雨 🌦",
            81: "中阵雨 🌧",
            82: "强阵雨 ⛈",
            95: "雷暴 ⛈",
            96: "雷暴伴小冰雹 ⛈",
            99: "雷暴伴大冰雹 ⛈",
        }
        return mapping.get(code, f"未知天气（代码 {code}）")


if __name__ == "__main__":
    root = tk.Tk()
    app = WeatherApp(root)
    root.mainloop()