import tkinter as tk
from tkinter import ttk, messagebox
import calendar
import json
import os
from datetime import datetime, timedelta

class CalendarApp:
    def __init__(self, root):
        self.root = root
        self.root.title("日历提醒与计划表")
        self.root.geometry("700x600")
        
        # 数据文件
        self.data_file = "plans.json"
        self.plans = self.load_plans()
        
        # 当前选中的日期（格式: YYYY-MM-DD）
        self.selected_date = datetime.now().strftime("%Y-%m-%d")
        # 当前显示的年份和月份
        self.current_year = datetime.now().year
        self.current_month = datetime.now().month
        
        # 已提醒过的计划（防止重复提醒）
        self.reminded_plans = set()
        
        # 构建界面
        self.create_widgets()
        self.update_calendar()
        self.show_plans_for_date(self.selected_date)
        
        # 启动提醒检查（每60秒）
        self.check_reminders()
        # 启动时立即提醒今日计划
        self.remind_today_plans()
        
    # ---------- 数据持久化 ----------
    def load_plans(self):
        if os.path.exists(self.data_file):
            with open(self.data_file, "r", encoding="utf-8") as f:
                return json.load(f)
        return {}
    
    def save_plans(self):
        with open(self.data_file, "w", encoding="utf-8") as f:
            json.dump(self.plans, f, indent=2, ensure_ascii=False)
    
    # ---------- 界面构建 ----------
    def create_widgets(self):
        # 顶部：年份/月份切换
        top_frame = tk.Frame(self.root, pady=10)
        top_frame.pack()
        
        tk.Button(top_frame, text="◀", command=self.prev_month).grid(row=0, column=0, padx=5)
        self.month_label = tk.Label(top_frame, text="", font=("Arial", 16, "bold"), width=15)
        self.month_label.grid(row=0, column=1)
        tk.Button(top_frame, text="▶", command=self.next_month).grid(row=0, column=2, padx=5)
        tk.Button(top_frame, text="今天", command=self.goto_today).grid(row=0, column=3, padx=10)
        
        # 日历网格（7列）
        self.cal_frame = tk.Frame(self.root)
        self.cal_frame.pack(pady=10)
        
        # 星期标题
        days = ["一", "二", "三", "四", "五", "六", "日"]
        for i, d in enumerate(days):
            tk.Label(self.cal_frame, text=d, font=("Arial", 10, "bold"), width=8, 
                     relief="ridge").grid(row=0, column=i, padx=1, pady=1)
        
        # 日期按钮（6行×7列）
        self.day_buttons = []
        for row in range(1, 7):
            row_buttons = []
            for col in range(7):
                btn = tk.Button(self.cal_frame, text="", width=8, height=2,
                                command=lambda r=row, c=col: self.on_day_click(r, c))
                btn.grid(row=row, column=col, padx=1, pady=1)
                row_buttons.append(btn)
            self.day_buttons.append(row_buttons)
        
        # 下半部分：计划显示与添加
        bottom_frame = tk.Frame(self.root)
        bottom_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=10)
        
        # 左：计划列表
        list_frame = tk.Frame(bottom_frame)
        list_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        
        tk.Label(list_frame, text="当天计划", font=("Arial", 12, "bold")).pack(anchor="w")
        
        self.plan_listbox = tk.Listbox(list_frame, height=10, width=40)
        self.plan_listbox.pack(fill=tk.BOTH, expand=True, pady=5)
        self.plan_listbox.bind("<Double-Button-1>", self.delete_plan)  # 双击删除
        
        # 右：添加计划
        add_frame = tk.Frame(bottom_frame, padx=10)
        add_frame.pack(side=tk.RIGHT, fill=tk.Y)
        
        tk.Label(add_frame, text="日期:", font=("Arial", 10)).grid(row=0, column=0, sticky="e", pady=2)
        self.date_entry = tk.Entry(add_frame, width=15)
        self.date_entry.grid(row=0, column=1, pady=2)
        self.date_entry.insert(0, self.selected_date)
        
        tk.Label(add_frame, text="时间 (HH:MM):", font=("Arial", 10)).grid(row=1, column=0, sticky="e", pady=2)
        self.time_entry = tk.Entry(add_frame, width=15)
        self.time_entry.grid(row=1, column=1, pady=2)
        self.time_entry.insert(0, datetime.now().strftime("%H:%M"))
        
        tk.Label(add_frame, text="描述:", font=("Arial", 10)).grid(row=2, column=0, sticky="ne", pady=2)
        self.desc_entry = tk.Entry(add_frame, width=20)
        self.desc_entry.grid(row=2, column=1, pady=2)
        
        tk.Button(add_frame, text="添加计划", command=self.add_plan).grid(row=3, column=0, columnspan=2, pady=10)
        tk.Button(add_frame, text="删除选中", command=self.delete_selected).grid(row=4, column=0, columnspan=2)
        
        # 状态栏
        self.status_var = tk.StringVar()
        self.status_var.set("就绪")
        status_bar = tk.Label(self.root, textvariable=self.status_var, relief="sunken", anchor="w")
        status_bar.pack(side=tk.BOTTOM, fill=tk.X)
    
    # ---------- 日历更新 ----------
    def update_calendar(self):
        # 更新月份标题
        self.month_label.config(text=f"{self.current_year}年{self.current_month}月")
        
        # 获取当月日历
        cal = calendar.monthcalendar(self.current_year, self.current_month)
        # 清空按钮
        for row in range(6):
            for col in range(7):
                self.day_buttons[row][col].config(text="", state="normal", bg="SystemButtonFace")
        
        # 填充日期
        for row, week in enumerate(cal):
            for col, day in enumerate(week):
                if day != 0:
                    btn = self.day_buttons[row][col]
                    btn.config(text=str(day))
                    # 标记今天
                    date_str = f"{self.current_year}-{self.current_month:02d}-{day:02d}"
                    if date_str == datetime.now().strftime("%Y-%m-%d"):
                        btn.config(bg="lightblue")
                    # 如果该日期有计划，标记颜色
                    if date_str in self.plans and self.plans[date_str]:
                        btn.config(bg="lightgreen" if date_str != datetime.now().strftime("%Y-%m-%d") else "lightblue")
    
    # ---------- 月份切换 ----------
    def prev_month(self):
        if self.current_month == 1:
            self.current_month = 12
            self.current_year -= 1
        else:
            self.current_month -= 1
        self.update_calendar()
    
    def next_month(self):
        if self.current_month == 12:
            self.current_month = 1
            self.current_year += 1
        else:
            self.current_month += 1
        self.update_calendar()
    
    def goto_today(self):
        self.current_year = datetime.now().year
        self.current_month = datetime.now().month
        self.update_calendar()
        self.selected_date = datetime.now().strftime("%Y-%m-%d")
        self.date_entry.delete(0, tk.END)
        self.date_entry.insert(0, self.selected_date)
        self.show_plans_for_date(self.selected_date)
    
    # ---------- 日期点击 ----------
    def on_day_click(self, row, col):
        btn = self.day_buttons[row][col]
        day_text = btn.cget("text")
        if day_text:
            date_str = f"{self.current_year}-{self.current_month:02d}-{int(day_text):02d}"
            self.selected_date = date_str
            self.date_entry.delete(0, tk.END)
            self.date_entry.insert(0, date_str)
            self.show_plans_for_date(date_str)
    
    # ---------- 计划显示 ----------
    def show_plans_for_date(self, date_str):
        self.plan_listbox.delete(0, tk.END)
        if date_str in self.plans:
            for plan in self.plans[date_str]:
                display = f"{plan.get('time', '')} - {plan.get('text', '')}"
                self.plan_listbox.insert(tk.END, display)
        self.status_var.set(f"当前显示: {date_str} 的计划")
    
    # ---------- 添加计划 ----------
    def add_plan(self):
        date_str = self.date_entry.get().strip()
        time_str = self.time_entry.get().strip()
        desc = self.desc_entry.get().strip()
        
        if not date_str or not desc:
            messagebox.showwarning("警告", "日期和描述不能为空！")
            return
        
        # 验证日期格式
        try:
            datetime.strptime(date_str, "%Y-%m-%d")
        except:
            messagebox.showwarning("警告", "日期格式错误，请使用 YYYY-MM-DD")
            return
        
        # 验证时间格式（可选）
        if time_str:
            try:
                datetime.strptime(time_str, "%H:%M")
            except:
                messagebox.showwarning("警告", "时间格式错误，请使用 HH:MM")
                return
        
        if date_str not in self.plans:
            self.plans[date_str] = []
        
        self.plans[date_str].append({"time": time_str, "text": desc})
        self.save_plans()
        self.update_calendar()
        self.show_plans_for_date(date_str)
        self.desc_entry.delete(0, tk.END)
        self.status_var.set(f"已添加计划: {desc}")
    
    # ---------- 删除计划（双击列表项） ----------
    def delete_plan(self, event=None):
        selection = self.plan_listbox.curselection()
        if not selection:
            return
        index = selection[0]
        date_str = self.selected_date
        if date_str in self.plans and index < len(self.plans[date_str]):
            plan = self.plans[date_str][index]
            if messagebox.askyesno("删除确认", f"确定要删除计划 '{plan.get('text', '')}' 吗？"):
                del self.plans[date_str][index]
                if not self.plans[date_str]:  # 如果该日期没有计划了，删除键
                    del self.plans[date_str]
                self.save_plans()
                self.update_calendar()
                self.show_plans_for_date(date_str)
                self.status_var.set("已删除计划")
    
    def delete_selected(self):
        self.delete_plan()
    
    # ---------- 提醒功能 ----------
    def check_reminders(self):
        """每分钟检查一次是否有计划需要提醒"""
        now = datetime.now()
        today_str = now.strftime("%Y-%m-%d")
        current_time = now.strftime("%H:%M")
        
        if today_str in self.plans:
            for plan in self.plans[today_str]:
                plan_time = plan.get("time", "")
                if plan_time and plan_time == current_time:
                    # 生成唯一标识以防止重复提醒
                    key = f"{today_str}_{plan_time}_{plan.get('text', '')}"
                    if key not in self.reminded_plans:
                        self.reminded_plans.add(key)
                        messagebox.showinfo("⏰ 提醒", f"时间到了！\n{plan_time} - {plan.get('text', '')}")
        
        # 每分钟再次调用
        self.root.after(60000, self.check_reminders)
    
    def remind_today_plans(self):
        """程序启动时提醒今日所有计划"""
        today_str = datetime.now().strftime("%Y-%m-%d")
        if today_str in self.plans and self.plans[today_str]:
            msg = "今日计划：\n" + "\n".join([f"{p.get('time', '')} - {p.get('text', '')}" for p in self.plans[today_str]])
            messagebox.showinfo("📅 今日提醒", msg)

# ---------- 启动程序 ----------
if __name__ == "__main__":
    root = tk.Tk()
    app = CalendarApp(root)
    root.mainloop()