import tkinter as tk
from tkinter import ttk, messagebox, filedialog
import json
import os
from datetime import datetime, date
import threading
import time

DATA_FILE = "birthdays.json"

# 星座表
ZODIAC = [
    (1, 19, "摩羯座"), (2, 18, "水瓶座"),
    (3, 20, "双鱼座"), (4, 19, "白羊座"),
    (5, 20, "金牛座"), (6, 21, "双子座"),
    (7, 22, "巨蟹座"), (8, 22, "狮子座"),
    (9, 22, "处女座"), (10, 23, "天秤座"),
    (11, 21, "天蝎座"), (12, 31, "射手座"),
]


def get_zodiac(month, day):
    for m, d, name in ZODIAC:
        if month == m and day <= d:
            return name
        if month == m + 1 and day > d:
            return name
    return "摩羯座"


def get_days_until(birth_month, birth_day):
    today = date.today()
    this_year = today.year
    next_birthday = date(this_year, birth_month, birth_day)
    if next_birthday < today:
        next_birthday = date(this_year + 1, birth_month, birth_day)
    return (next_birthday - today).days


class BirthdayApp:
    def __init__(self, root):
        self.root = root
        self.root.title("033. 生日提醒器")
        self.root.geometry("700x520")
        self.root.resizable(False, False)

        self.birthdays = []
        self.load_data()

        self.build_ui()
        self.refresh_list()
        self.start_reminder()

    # ========== 数据持久化 ==========
    def load_data(self):
        if os.path.exists(DATA_FILE):
            try:
                with open(DATA_FILE, "r", encoding="utf-8") as f:
                    self.birthdays = json.load(f)
            except:
                self.birthdays = []

    def save_data(self):
        with open(DATA_FILE, "w", encoding="utf-8") as f:
            json.dump(self.birthdays, f, ensure_ascii=False, indent=2)

    # ========== UI ==========
    def build_ui(self):
        # 标题
        tk.Label(
            self.root, text="🎂 生日提醒器",
            font=("微软雅黑", 18, "bold")
        ).pack(pady=10)

        # ===== 添加区 =====
        add_frame = tk.LabelFrame(self.root, text="添加生日", font=("微软雅黑", 10))
        add_frame.pack(padx=20, pady=5, fill=tk.X)

        tk.Label(add_frame, text="姓名：").grid(row=0, column=0, padx=5, pady=5, sticky="e")
        self.name_entry = tk.Entry(add_frame, width=12, font=("微软雅黑", 10))
        self.name_entry.grid(row=0, column=1, padx=5, pady=5)

        tk.Label(add_frame, text="月份：").grid(row=0, column=2, padx=5, pady=5, sticky="e")
        self.month_spin = tk.Spinbox(add_frame, from_=1, to=12, width=4)
        self.month_spin.grid(row=0, column=3, padx=5, pady=5)
        self.month_spin.delete(0, tk.END)
        self.month_spin.insert(0, str(datetime.now().month))

        tk.Label(add_frame, text="日期：").grid(row=0, column=4, padx=5, pady=5, sticky="e")
        self.day_spin = tk.Spinbox(add_frame, from_=1, to=31, width=4)
        self.day_spin.grid(row=0, column=5, padx=5, pady=5)
        self.day_spin.delete(0, tk.END)
        self.day_spin.insert(0, str(datetime.now().day))

        tk.Button(
            add_frame, text="添加",
            bg="#4CAF50", fg="white", width=8,
            command=self.add_birthday
        ).grid(row=0, column=6, padx=10, pady=5)

        # ===== 列表区 =====
        list_frame = tk.LabelFrame(self.root, text="生日列表", font=("微软雅黑", 10))
        list_frame.pack(padx=20, pady=10, fill=tk.BOTH, expand=True)

        # 表头
        headers = ["姓名", "生日", "星座", "年龄", "倒计时", "操作"]
        widths = [12, 10, 10, 6, 10, 8]
        for i, (h, w) in enumerate(zip(headers, widths)):
            tk.Label(list_frame, text=h, font=("微软雅黑", 9, "bold"),
                      width=w).grid(row=0, column=i, padx=2, pady=2)

        # 列表容器
        self.list_container = tk.Frame(list_frame)
        self.list_container.grid(row=1, column=0, columnspan=6, padx=5, pady=2)

        # ===== 底部按钮 =====
        btn_frame = tk.Frame(self.root)
        btn_frame.pack(pady=5)

        tk.Button(btn_frame, text="导入", width=8,
                  command=self.import_data).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="导出", width=8,
                  command=self.export_data).pack(side=tk.LEFT, padx=5)
        tk.Button(btn_frame, text="刷新", width=8,
                  command=self.refresh_list).pack(side=tk.LEFT, padx=5)

        # ===== 状态栏 =====
        self.status = tk.Label(
            self.root, text="", anchor="w", relief=tk.SUNKEN
        )
        self.status.pack(side=tk.BOTTOM, fill=tk.X)
        self.update_status()

    def refresh_list(self):
        # 清空旧数据
        for w in self.list_container.winfo_children():
            w.destroy()

        self.birthdays.sort(key=lambda x: (x["month"], x["day"]))

        for i, b in enumerate(self.birthdays):
            name = b["name"]
            month, day = b["month"], b["day"]
            zodiac = get_zodiac(month, day)
            days_until = get_days_until(month, day)

            # 计算年龄
            today = date.today()
            age = today.year - b.get("year", today.year) - \
                   ((today.month, today.day) < (month, day))

            # 高亮当天生日
            bg = "#FFF3E0" if days_until == 0 else "#FFFFFF"

            row = tk.Frame(self.list_container, bg=bg)
            row.pack(fill=tk.X)

            tk.Label(row, text=name, width=12, bg=bg,
                     font=("微软雅黑", 9)).pack(side=tk.LEFT, padx=2)
            tk.Label(row, text=f"{month:02d}-{day:02d}", width=10, bg=bg,
                     font=("Consolas", 9)).pack(side=tk.LEFT, padx=2)

            zl = tk.Label(row, text=zodiac, width=10, bg=bg,
                           font=("微软雅黑", 9))
            zl.pack(side=tk.LEFT, padx=2)

            tk.Label(row, text=f"{age}岁" if age > 0 else "?", width=6, bg=bg,
                     font=("微软雅黑", 9)).pack(side=tk.LEFT, padx=2)

            if days_until == 0:
                tl = tk.Label(row, text="🎂 今天！", width=10, bg=bg,
                              font=("微软雅黑", 9, "bold"), fg="#E53935")
            elif days_until == 1:
                tl = tk.Label(row, text="明天！", width=10, bg=bg,
                              font=("微软雅黑", 9), fg="#FF9800")
            else:
                tl = tk.Label(row, text=f"{days_until}天", width=10, bg=bg,
                              font=("Consolas", 9))
            tl.pack(side=tk.LEFT, padx=2)

            tk.Button(row, text="删除", width=6,
                      command=lambda idx=i: self.delete_birthday(idx)
                      ).pack(side=tk.LEFT, padx=5)

        self.update_status()

    def update_status(self):
        today_count = sum(1 for b in self.birthdays
                          if b["month"] == datetime.now().month and
                          b["day"] == datetime.now().day)
        total = len(self.birthdays)
        text = f"共 {total} 条记录"
        if today_count > 0:
            text += f"  |  🎂 今天有 {today_count} 人生日！"
        self.status.config(text=text)

    def add_birthday(self):
        name = self.name_entry.get().strip()
        try:
            month = int(self.month_spin.get())
            day = int(self.day_spin.get())
        except ValueError:
            messagebox.showwarning("提示", "请输入有效的日期！")
            return

        if not name:
            messagebox.showwarning("提示", "请输入姓名！")
            return

        if not (1 <= month <= 12 and 1 <= day <= 31):
            messagebox.showwarning("提示", "日期不合法！")
            return

        # 检查重复
        for b in self.birthdays:
            if b["name"] == name and b["month"] == month and b["day"] == day:
                messagebox.showwarning("提示", "该生日已存在！")
                return

        self.birthdays.append({
            "name": name,
            "month": month,
            "day": day,
            "year": datetime.now().year
        })
        self.save_data()
        self.name_entry.delete(0, tk.END)
        self.refresh_list()
        messagebox.showinfo("成功", f"已添加 {name} 的生日！")

    def delete_birthday(self, idx):
        if messagebox.askyesno("确认", f"删除 {self.birthdays[idx]['name']} 的生日？"):
            self.birthdays.pop(idx)
            self.save_data()
            self.refresh_list()

    def import_data(self):
        path = filedialog.askopenfilename(
            filetypes=[("JSON 文件", "*.json"), ("文本文件", "*.txt")]
        )
        if not path:
            return
        try:
            with open(path, "r", encoding="utf-8") as f:
                self.birthdays = json.load(f)
            self.save_data()
            self.refresh_list()
            messagebox.showinfo("成功", "导入完成！")
        except Exception as e:
            messagebox.showerror("错误", f"导入失败：{e}")

    def export_data(self):
        path = filedialog.asksaveasfilename(
            defaultextension=".json",
            filetypes=[("JSON 文件", "*.json")]
        )
        if not path:
            return
        with open(path, "w", encoding="utf-8") as f:
            json.dump(self.birthdays, f, ensure_ascii=False, indent=2)
        messagebox.showinfo("成功", "导出完成！")

    def start_reminder(self):
        def check():
            while True:
                today = date.today()
                for b in self.birthdays:
                    if b["month"] == today.month and b["day"] == today.day:
                        self.root.after(0, lambda n=b["name"]: messagebox.showinfo(
                            "🎂 生日提醒", f"今天是 {n} 的生日！\n别忘了送上祝福哦～"
                        ))
                time.sleep(3600)  # 每小时检查一次

        threading.Thread(target=check, daemon=True).start()


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