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

# ---------------------------- 主题配色方案 ----------------------------
THEMES = {
    "默认简约": {
        "bg": "#f5f5f5",
        "fg": "#333333",
        "frame_bg": "#ffffff",
        "button_bg": "#e0e0e0",
        "button_fg": "#333333",
        "accent": "#4CAF50",
        "week_bg": "#dddddd",
        "week_fg": "#222222",
        "empty_day_bg": "#fafafa",
        "empty_day_fg": "#999999",
        "normal_day_bg": "#ffffff",
        "normal_day_fg": "#333333",
        "plan_mark_bg": "#c8e6c9",   # 有计划的日期标记色
        "selected_bg": "#ffcc00",
        "selected_fg": "#000000",
    },
    "清新绿": {
        "bg": "#e8f5e9",
        "fg": "#2e7d32",
        "frame_bg": "#ffffff",
        "button_bg": "#c8e6c9",
        "button_fg": "#1b5e20",
        "accent": "#43a047",
        "week_bg": "#a5d6a7",
        "week_fg": "#1b5e20",
        "empty_day_bg": "#f1f8e9",
        "empty_day_fg": "#81c784",
        "normal_day_bg": "#ffffff",
        "normal_day_fg": "#2e7d32",
        "plan_mark_bg": "#b9f6ca",
        "selected_bg": "#ffb74d",
        "selected_fg": "#3e2723",
    },
    "海洋蓝": {
        "bg": "#e3f2fd",
        "fg": "#0d47a1",
        "frame_bg": "#ffffff",
        "button_bg": "#bbdefb",
        "button_fg": "#0d47a1",
        "accent": "#1e88e5",
        "week_bg": "#90caf9",
        "week_fg": "#0d47a1",
        "empty_day_bg": "#e1f5fe",
        "empty_day_fg": "#4fc3f7",
        "normal_day_bg": "#ffffff",
        "normal_day_fg": "#0d47a1",
        "plan_mark_bg": "#b3e5fc",
        "selected_bg": "#ffb74d",
        "selected_fg": "#3e2723",
    },
    "暖阳橙": {
        "bg": "#fff3e0",
        "fg": "#e65100",
        "frame_bg": "#ffffff",
        "button_bg": "#ffe0b2",
        "button_fg": "#e65100",
        "accent": "#fb8c00",
        "week_bg": "#ffcc80",
        "week_fg": "#e65100",
        "empty_day_bg": "#fff8e1",
        "empty_day_fg": "#ffb74d",
        "normal_day_bg": "#ffffff",
        "normal_day_fg": "#e65100",
        "plan_mark_bg": "#ffe0b2",
        "selected_bg": "#ffb74d",
        "selected_fg": "#3e2723",
    },
    "深色模式": {
        "bg": "#2d2d2d",
        "fg": "#eeeeee",
        "frame_bg": "#3c3f41",
        "button_bg": "#4c5052",
        "button_fg": "#ffffff",
        "accent": "#689f38",
        "week_bg": "#555555",
        "week_fg": "#ffffff",
        "empty_day_bg": "#3c3f41",
        "empty_day_fg": "#aaaaaa",
        "normal_day_bg": "#4c5052",
        "normal_day_fg": "#eeeeee",
        "plan_mark_bg": "#5a6e41",
        "selected_bg": "#f9a825",
        "selected_fg": "#000000",
    }
}

# ---------------------------- 数据模型 ----------------------------
class PlanManager:
    """管理计划数据，包括加载、保存、增删改查"""
    DATA_FILE = "calendar_plans.json"

    def __init__(self):
        self.plans = []  # 每个计划: dict{id, title, datetime_obj, reminder_enabled, reminded}
        self.next_id = 1
        self.load_data()

    def load_data(self):
        if os.path.exists(self.DATA_FILE):
            try:
                with open(self.DATA_FILE, 'r', encoding='utf-8') as f:
                    data = json.load(f)
                    self.plans = []
                    for item in data:
                        dt = datetime.datetime.fromisoformat(item['datetime_str'])
                        plan = {
                            'id': item['id'],
                            'title': item['title'],
                            'datetime_obj': dt,
                            'reminder_enabled': item['reminder_enabled'],
                            'reminded': item['reminded']
                        }
                        self.plans.append(plan)
                    if self.plans:
                        self.next_id = max(p['id'] for p in self.plans) + 1
                    else:
                        self.next_id = 1
            except Exception as e:
                print(f"加载数据出错: {e}")
                self.plans = []
                self.next_id = 1

    def save_data(self):
        data = []
        for plan in self.plans:
            data.append({
                'id': plan['id'],
                'title': plan['title'],
                'datetime_str': plan['datetime_obj'].isoformat(),
                'reminder_enabled': plan['reminder_enabled'],
                'reminded': plan['reminded']
            })
        try:
            with open(self.DATA_FILE, 'w', encoding='utf-8') as f:
                json.dump(data, f, indent=2, ensure_ascii=False)
        except Exception as e:
            print(f"保存数据出错: {e}")

    def add_plan(self, title, datetime_obj, reminder_enabled):
        plan = {
            'id': self.next_id,
            'title': title,
            'datetime_obj': datetime_obj,
            'reminder_enabled': reminder_enabled,
            'reminded': False
        }
        self.plans.append(plan)
        self.next_id += 1
        self.save_data()
        return plan

    def update_plan(self, plan_id, title, datetime_obj, reminder_enabled):
        for plan in self.plans:
            if plan['id'] == plan_id:
                plan['title'] = title
                plan['datetime_obj'] = datetime_obj
                plan['reminder_enabled'] = reminder_enabled
                plan['reminded'] = False
                self.save_data()
                return True
        return False

    def delete_plan(self, plan_id):
        for i, plan in enumerate(self.plans):
            if plan['id'] == plan_id:
                del self.plans[i]
                self.save_data()
                return True
        return False

    def get_plans_for_date(self, date):
        day_plans = []
        for plan in self.plans:
            plan_date = plan['datetime_obj'].date()
            if plan_date == date:
                day_plans.append(plan)
        day_plans.sort(key=lambda p: p['datetime_obj'].time())
        return day_plans

    def get_plans_for_reminder_check(self):
        return [p for p in self.plans if p['reminder_enabled'] and not p['reminded']]

    def mark_reminded(self, plan_id):
        for plan in self.plans:
            if plan['id'] == plan_id:
                plan['reminded'] = True
                self.save_data()
                break


# ---------------------------- GUI 应用 ----------------------------
class CalendarApp:
    def __init__(self, root):
        self.root = root
        self.root.title("📅 简约日历·计划表")
        self.root.geometry("1050x680")
        self.root.resizable(True, True)

        # 数据管理
        self.plan_manager = PlanManager()

        # 当前显示的年/月/选中日期
        self.current_year = datetime.datetime.now().year
        self.current_month = datetime.datetime.now().month
        self.selected_date = datetime.datetime.now().date()

        # 当前主题
        self.current_theme = "默认简约"
        self.theme = THEMES[self.current_theme]

        # 应用整体背景色
        self.root.configure(bg=self.theme["bg"])

        # 左右主框架
        self.left_frame = tk.Frame(root, bg=self.theme["frame_bg"], relief=tk.FLAT, bd=0)
        self.left_frame.pack(side=tk.LEFT, fill=tk.BOTH, expand=True, padx=10, pady=10)
        self.right_frame = tk.Frame(root, bg=self.theme["frame_bg"], relief=tk.FLAT, bd=0)
        self.right_frame.pack(side=tk.RIGHT, fill=tk.BOTH, expand=True, padx=10, pady=10)

        # 顶部主题选择栏（简约风格）
        self.top_bar = tk.Frame(root, bg=self.theme["bg"], height=40)
        self.top_bar.pack(side=tk.TOP, fill=tk.X, padx=10, pady=(5,0))
        tk.Label(self.top_bar, text="🎨 颜色滤镜", bg=self.theme["bg"], fg=self.theme["fg"],
                 font=("Arial", 10)).pack(side=tk.LEFT, padx=5)
        self.theme_var = tk.StringVar(value=self.current_theme)
        theme_menu = ttk.Combobox(self.top_bar, textvariable=self.theme_var, values=list(THEMES.keys()),
                                  state="readonly", width=12)
        theme_menu.pack(side=tk.LEFT, padx=5)
        theme_menu.bind("<<ComboboxSelected>>", self.on_theme_change)

        # 构建界面
        self._build_calendar_header()
        self._build_right_panel()

        # 刷新日历
        self.refresh_calendar()

        # 提醒检查
        self.check_reminders()
        self.root.after(60000, self.schedule_reminder_check)

    # ------------------------- 主题切换 -------------------------
    def on_theme_change(self, event=None):
        new_theme = self.theme_var.get()
        if new_theme != self.current_theme:
            self.current_theme = new_theme
            self.theme = THEMES[new_theme]
            self.apply_theme()

    def apply_theme(self):
        """应用当前主题配色到所有控件"""
        self.root.configure(bg=self.theme["bg"])
        self.top_bar.configure(bg=self.theme["bg"])
        for child in self.top_bar.winfo_children():
            if isinstance(child, tk.Label):
                child.configure(bg=self.theme["bg"], fg=self.theme["fg"])
        self.left_frame.configure(bg=self.theme["frame_bg"])
        self.right_frame.configure(bg=self.theme["frame_bg"])
        # 重新构建日历和右侧面板（保证所有按钮颜色更新）
        self._rebuild_calendar_with_attributes()
        self._refresh_right_panel_static()
        self.refresh_plans_list()

    def _refresh_right_panel_static(self):
        """刷新右侧面板的静态控件（按钮、标签背景）"""
        self.date_label.configure(bg=self.theme["frame_bg"], fg=self.theme["fg"])
        # 右侧按钮区域
        for widget in self.btn_frame.winfo_children():
            if isinstance(widget, tk.Button):
                widget.configure(bg=self.theme["button_bg"], fg=self.theme["button_fg"],
                                 activebackground=self.theme["accent"], activeforeground="white")

    # ------------------------- 左侧日历 -------------------------
    def _build_calendar_header(self):
        header = tk.Frame(self.left_frame, bg=self.theme["frame_bg"])
        header.pack(pady=10)

        self.month_label = tk.Label(header, text="", font=("Arial", 16, "bold"),
                                    bg=self.theme["frame_bg"], fg=self.theme["fg"])
        self.month_label.pack(side=tk.LEFT, padx=20)

        btn_prev = tk.Button(header, text="◀ 上月", command=self.prev_month,
                             bg=self.theme["button_bg"], fg=self.theme["button_fg"],
                             relief=tk.FLAT, padx=8)
        btn_prev.pack(side=tk.LEFT, padx=5)
        btn_next = tk.Button(header, text="下月 ▶", command=self.next_month,
                             bg=self.theme["button_bg"], fg=self.theme["button_fg"],
                             relief=tk.FLAT, padx=8)
        btn_next.pack(side=tk.LEFT, padx=5)

        year_frame = tk.Frame(self.left_frame, bg=self.theme["frame_bg"])
        year_frame.pack(pady=5)
        tk.Label(year_frame, text="年份:", bg=self.theme["frame_bg"], fg=self.theme["fg"]).pack(side=tk.LEFT)
        self.year_spinbox = tk.Spinbox(year_frame, from_=1900, to=3000, width=6,
                                       command=self.on_year_change, font=("Arial", 10),
                                       bg=self.theme["button_bg"], fg=self.theme["button_fg"])
        self.year_spinbox.pack(side=tk.LEFT, padx=5)
        self.year_spinbox.delete(0, tk.END)
        self.year_spinbox.insert(0, str(self.current_year))

        # 星期标题
        week_header = tk.Frame(self.left_frame, bg=self.theme["frame_bg"])
        week_header.pack(pady=5)
        weeks = ["一", "二", "三", "四", "五", "六", "日"]
        for w in weeks:
            lbl = tk.Label(week_header, text=w, width=6, height=2,
                           font=("Arial", 10, "bold"),
                           bg=self.theme["week_bg"], fg=self.theme["week_fg"],
                           relief=tk.FLAT)
            lbl.pack(side=tk.LEFT, padx=1, pady=1)

        self.calendar_frame = tk.Frame(self.left_frame, bg=self.theme["frame_bg"])
        self.calendar_frame.pack(fill=tk.BOTH, expand=True)

    def _rebuild_calendar_with_attributes(self):
        """重新构建日历，每个按钮保存date属性，使用当前主题"""
        for widget in self.calendar_frame.winfo_children():
            widget.destroy()

        first_day_weekday = datetime.date(self.current_year, self.current_month, 1).weekday()
        _, num_days = calendar.monthrange(self.current_year, self.current_month)

        prev_month = datetime.date(self.current_year, self.current_month, 1) - datetime.timedelta(days=1)
        prev_month_days = prev_month.day
        prev_month_year = prev_month.year
        prev_month_month = prev_month.month

        row, col = 0, 0
        empty_cells = first_day_weekday
        for _ in range(empty_cells):
            day_num = prev_month_days - empty_cells + _ + 1
            date_obj = datetime.date(prev_month_year, prev_month_month, day_num)
            btn = tk.Button(self.calendar_frame, text=str(day_num), width=6, height=3,
                            bg=self.theme["empty_day_bg"], fg=self.theme["empty_day_fg"],
                            relief=tk.FLAT)
            btn.date = date_obj
            btn.config(command=lambda d=date_obj: self.on_date_selected(d))
            btn.grid(row=row, column=col, padx=1, pady=1)
            col += 1
            if col > 6:
                col = 0
                row += 1

        for day in range(1, num_days + 1):
            date_obj = datetime.date(self.current_year, self.current_month, day)
            plans_of_day = self.plan_manager.get_plans_for_date(date_obj)
            bg_color = self.theme["plan_mark_bg"] if plans_of_day else self.theme["normal_day_bg"]
            btn = tk.Button(self.calendar_frame, text=str(day), width=6, height=3,
                            bg=bg_color, fg=self.theme["normal_day_fg"],
                            relief=tk.FLAT)
            btn.date = date_obj
            btn.config(command=lambda d=date_obj: self.on_date_selected(d))
            btn.grid(row=row, column=col, padx=1, pady=1)
            col += 1
            if col > 6:
                col = 0
                row += 1

        total_cells = 6 * 7
        filled_cells = empty_cells + num_days
        remaining = total_cells - filled_cells
        next_month_date = datetime.date(self.current_year, self.current_month, num_days) + datetime.timedelta(days=1)
        next_month_year = next_month_date.year
        next_month_month = next_month_date.month
        for day in range(1, remaining + 1):
            date_obj = datetime.date(next_month_year, next_month_month, day)
            btn = tk.Button(self.calendar_frame, text=str(day), width=6, height=3,
                            bg=self.theme["empty_day_bg"], fg=self.theme["empty_day_fg"],
                            relief=tk.FLAT)
            btn.date = date_obj
            btn.config(command=lambda d=date_obj: self.on_date_selected(d))
            btn.grid(row=row, column=col, padx=1, pady=1)
            col += 1
            if col > 6:
                col = 0
                row += 1

        self._highlight_button_by_date(self.selected_date)

    def _highlight_button_by_date(self, target_date):
        for widget in self.calendar_frame.winfo_children():
            if isinstance(widget, tk.Button) and hasattr(widget, 'date') and widget.date == target_date:
                widget.config(bg=self.theme["selected_bg"], fg=self.theme["selected_fg"])
            else:
                if hasattr(widget, 'date'):
                    plans_exist = self.plan_manager.get_plans_for_date(widget.date)
                    if widget.date.month == self.current_month:
                        if plans_exist:
                            bg = self.theme["plan_mark_bg"]
                        else:
                            bg = self.theme["normal_day_bg"]
                        fg = self.theme["normal_day_fg"]
                    else:
                        bg = self.theme["empty_day_bg"]
                        fg = self.theme["empty_day_fg"]
                    widget.config(bg=bg, fg=fg)

    def refresh_calendar(self):
        self._rebuild_calendar_with_attributes()
        self.month_label.config(text=f"{self.current_year}年{self.current_month}月")
        self.year_spinbox.delete(0, tk.END)
        self.year_spinbox.insert(0, str(self.current_year))

    def on_year_change(self):
        try:
            new_year = int(self.year_spinbox.get())
            if new_year != self.current_year:
                self.current_year = new_year
                self.refresh_calendar()
        except:
            pass

    def prev_month(self):
        if self.current_month == 1:
            self.current_month = 12
            self.current_year -= 1
        else:
            self.current_month -= 1
        self.refresh_calendar()

    def next_month(self):
        if self.current_month == 12:
            self.current_month = 1
            self.current_year += 1
        else:
            self.current_month += 1
        self.refresh_calendar()

    def on_date_selected(self, date_obj):
        self.selected_date = date_obj
        if date_obj.year != self.current_year or date_obj.month != self.current_month:
            self.current_year = date_obj.year
            self.current_month = date_obj.month
            self.refresh_calendar()
        else:
            self._highlight_button_by_date(date_obj)
        self.refresh_plans_list()

    # ------------------------- 右侧计划面板 -------------------------
    def _build_right_panel(self):
        self.date_label = tk.Label(self.right_frame, text="", font=("Arial", 14, "bold"),
                                   bg=self.theme["frame_bg"], fg=self.theme["fg"])
        self.date_label.pack(pady=10)

        list_frame = tk.Frame(self.right_frame, bg=self.theme["frame_bg"])
        list_frame.pack(fill=tk.BOTH, expand=True, padx=10, pady=5)

        scrollbar = tk.Scrollbar(list_frame)
        scrollbar.pack(side=tk.RIGHT, fill=tk.Y)

        self.plans_listbox = tk.Listbox(list_frame, yscrollcommand=scrollbar.set,
                                        font=("Arial", 10), bg=self.theme["normal_day_bg"],
                                        fg=self.theme["fg"], selectbackground=self.theme["accent"])
        self.plans_listbox.pack(side=tk.LEFT, fill=tk.BOTH, expand=True)
        scrollbar.config(command=self.plans_listbox.yview)

        self.btn_frame = tk.Frame(self.right_frame, bg=self.theme["frame_bg"])
        self.btn_frame.pack(pady=10)

        for text, cmd, color in [("➕ 添加计划", self.add_plan_dialog, "#4CAF50"),
                                  ("✏️ 编辑计划", self.edit_plan, "#2196F3"),
                                  ("🗑️ 删除计划", self.delete_plan, "#f44336")]:
            btn = tk.Button(self.btn_frame, text=text, command=cmd,
                            bg=self.theme["button_bg"], fg=self.theme["button_fg"],
                            relief=tk.FLAT, width=12)
            btn.pack(side=tk.LEFT, padx=5)
        self.refresh_plans_list()

    def refresh_plans_list(self):
        self.date_label.config(text=f"📌 {self.selected_date.year}年{self.selected_date.month}月{self.selected_date.day}日 的计划")
        plans = self.plan_manager.get_plans_for_date(self.selected_date)
        self.plans_listbox.delete(0, tk.END)
        if not plans:
            self.plans_listbox.insert(tk.END, "✨ 暂无计划，点击添加 ✨")
        else:
            for plan in plans:
                time_str = plan['datetime_obj'].strftime("%H:%M")
                reminder_flag = "🔔" if plan['reminder_enabled'] else "⭕"
                display_text = f"{reminder_flag} {time_str}  {plan['title']}"
                self.plans_listbox.insert(tk.END, display_text)
        self.current_plans_mapping = {}
        for idx, plan in enumerate(plans):
            self.current_plans_mapping[idx] = plan['id']

    def get_selected_plan_id(self):
        selection = self.plans_listbox.curselection()
        if not selection:
            messagebox.showwarning("提示", "请先选择一个计划")
            return None
        idx = selection[0]
        if idx not in self.current_plans_mapping:
            return None
        return self.current_plans_mapping[idx]

    def add_plan_dialog(self):
        self._plan_form_dialog(title="添加计划", is_edit=False)

    def edit_plan(self):
        plan_id = self.get_selected_plan_id()
        if plan_id is None:
            return
        plan = next((p for p in self.plan_manager.plans if p['id'] == plan_id), None)
        if plan:
            self._plan_form_dialog(title="编辑计划", is_edit=True, plan=plan)

    def _plan_form_dialog(self, title, is_edit=False, plan=None):
        dialog = tk.Toplevel(self.root)
        dialog.title(title)
        dialog.geometry("400x300")
        dialog.resizable(False, False)
        dialog.transient(self.root)
        dialog.grab_set()
        dialog.configure(bg=self.theme["bg"])

        tk.Label(dialog, text="计划标题:", font=("Arial", 10), bg=self.theme["bg"], fg=self.theme["fg"]).pack(pady=(15, 0))
        title_entry = tk.Entry(dialog, width=40, font=("Arial", 10),
                               bg=self.theme["normal_day_bg"], fg=self.theme["fg"])
        title_entry.pack(pady=5)

        tk.Label(dialog, text="日期时间 (YYYY-MM-DD HH:MM):", font=("Arial", 10),
                 bg=self.theme["bg"], fg=self.theme["fg"]).pack(pady=(10, 0))
        time_entry = tk.Entry(dialog, width=40, font=("Arial", 10),
                              bg=self.theme["normal_day_bg"], fg=self.theme["fg"])
        time_entry.pack(pady=5)

        if is_edit and plan:
            default_time = plan['datetime_obj'].strftime("%Y-%m-%d %H:%M")
            time_entry.insert(0, default_time)
            title_entry.insert(0, plan['title'])
        else:
            default_time = f"{self.selected_date.year}-{self.selected_date.month:02d}-{self.selected_date.day:02d} 09:00"
            time_entry.insert(0, default_time)

        reminder_var = tk.BooleanVar(value=(plan['reminder_enabled'] if is_edit and plan else True))
        remind_check = tk.Checkbutton(dialog, text="启用提醒", variable=reminder_var,
                                      bg=self.theme["bg"], fg=self.theme["fg"],
                                      selectcolor=self.theme["bg"])
        remind_check.pack(pady=10)

        def save_plan():
            title_str = title_entry.get().strip()
            time_str = time_entry.get().strip()
            if not title_str:
                messagebox.showwarning("警告", "计划标题不能为空")
                return
            try:
                dt = datetime.datetime.strptime(time_str, "%Y-%m-%d %H:%M")
            except ValueError:
                messagebox.showwarning("警告", "时间格式错误，请使用 YYYY-MM-DD HH:MM 格式")
                return
            if is_edit and plan:
                self.plan_manager.update_plan(plan['id'], title_str, dt, reminder_var.get())
                messagebox.showinfo("成功", "计划已更新")
            else:
                self.plan_manager.add_plan(title_str, dt, reminder_var.get())
                messagebox.showinfo("成功", "计划已添加")
            dialog.destroy()
            self.refresh_calendar()
            self.refresh_plans_list()

        btn_frame = tk.Frame(dialog, bg=self.theme["bg"])
        btn_frame.pack(pady=20)
        tk.Button(btn_frame, text="保存", command=save_plan, width=10,
                  bg=self.theme["accent"], fg="white", relief=tk.FLAT).pack(side=tk.LEFT, padx=10)
        tk.Button(btn_frame, text="取消", command=dialog.destroy, width=10,
                  bg=self.theme["button_bg"], fg=self.theme["button_fg"], relief=tk.FLAT).pack(side=tk.LEFT)

    def delete_plan(self):
        plan_id = self.get_selected_plan_id()
        if plan_id is None:
            return
        if messagebox.askyesno("确认删除", "确定要删除该计划吗？"):
            self.plan_manager.delete_plan(plan_id)
            self.refresh_calendar()
            self.refresh_plans_list()

    # ------------------------- 提醒 -------------------------
    def check_reminders(self):
        now = datetime.datetime.now()
        plans_to_remind = self.plan_manager.get_plans_for_reminder_check()
        reminded_ids = []
        for plan in plans_to_remind:
            plan_time = plan['datetime_obj']
            time_diff = (now - plan_time).total_seconds()
            if time_diff > 60:
                self.plan_manager.mark_reminded(plan['id'])
                continue
            if -30 <= time_diff <= 60:
                msg = f"🔔 提醒：{plan['title']}\n时间：{plan_time.strftime('%Y-%m-%d %H:%M')}"
                messagebox.showinfo("计划提醒", msg)
                reminded_ids.append(plan['id'])
        for pid in reminded_ids:
            self.plan_manager.mark_reminded(pid)
        self.refresh_calendar()
        self.refresh_plans_list()

    def schedule_reminder_check(self):
        self.check_reminders()
        self.root.after(60000, self.schedule_reminder_check)


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