# -*- coding: utf-8 -*-
"""
随机点名器 v2.0
================
新增功能（相比 v1.1）：
  7. 已抽列显示 √
  8. 名单列表点击列头排序
  9. 快捷键增强（F9开始 F10停止 F11重置）
 10. 点名结果大号弹显3秒后自动缩小
 11. 批量导入时自动去重
 12. 点名历史支持点击删除单条
 13. 统计页增加饼图（文字柱状图）
 14. 支持拖拽TXT文件到窗口导入
 15. 暗色/亮色主题切换
 16. 名单搜索过滤
纯 tkinter + 标准库
"""

import tkinter as tk
from tkinter import ttk, messagebox, filedialog, simpledialog
import random
import csv
import time
import os
from datetime import datetime
from dataclasses import dataclass, asdict, fields
from typing import List, Optional

try:
    import winsound
    _HAS_SOUND = True
except ImportError:
    _HAS_SOUND = False


# ============================================================
# 数据模型
# ============================================================

@dataclass
class Person:
    name: str = ""
    weight: float = 1.0
    group: str = ""
    drawn: bool = False

    def to_dict(self) -> dict:
        return asdict(self)

    @classmethod
    def from_dict(cls, d: dict) -> 'Person':
        allowed = {f.name for f in fields(cls)}
        return cls(**{k: v for k, v in d.items() if k in allowed})


@dataclass
class DrawRecord:
    name: str = ""
    timestamp: str = ""
    mode: str = ""


# ============================================================
# 名单池
# ============================================================

class NamePool:
    def __init__(self, people: Optional[List[Person]] = None):
        self.people: List[Person] = people if people is not None else []

    def add(self, person: Person):
        # 去重（新增11）
        if any(p.name == person.name for p in self.people):
            return False
        self.people.append(person)
        return True

    def available(self) -> List[Person]:
        return [p for p in self.people if not p.drawn]

    def reset_drawn(self):
        for p in self.people:
            p.drawn = False

    def search(self, keyword: str) -> List[Person]:
        """搜索过滤（新增16）"""
        kw = keyword.lower()
        return [p for p in self.people if kw in p.name.lower()]

    @classmethod
    def from_txt(cls, filepath: str) -> 'NamePool':
        pool = cls()
        with open(filepath, "r", encoding="utf-8") as f:
            for line in f:
                name = line.strip()
                if name and not name.startswith("#"):
                    pool.add(Person(name=name))
        return pool

    @classmethod
    def from_csv(cls, filepath: str) -> 'NamePool':
        pool = cls()
        with open(filepath, "r", encoding="utf-8-sig", newline="") as f:
            reader = csv.reader(f)
            header = next(reader, None)
            has_weight = header and "weight" in [h.lower() for h in header]
            f.seek(0)
            if has_weight:
                for row in csv.DictReader(f):
                    name = (row.get("name") or row.get("姓名") or "").strip()
                    if not name: continue
                    w = float(row.get("weight") or row.get("权重") or 1.0)
                    pool.add(Person(name=name, weight=w))
            else:
                for row in reader:
                    if not row: continue
                    name = row[0].strip()
                    if name and name not in ("name", "姓名"):
                        pool.add(Person(name=name))
        return pool


# ============================================================
# 主窗口
# ============================================================

class NamePickerApp:
    def __init__(self, root: tk.Tk):
        self.root = root
        self.root.title("🎯 随机点名器 v2.0")
        self.root.geometry("1700x700")
        self.root.minsize(1000, 600)

        self.pool = NamePool()
        self.history: List[DrawRecord] = []
        self.priority_mode = tk.BooleanVar(value=True)
        self.allow_repeat = tk.BooleanVar(value=False)
        self.sound_enabled = True
        self.blink_enabled = True
        self.dark_theme = True  # 默认暗色（新增15）

        self._animating = False
        self._anim_start = 0.0
        self._anim_duration = 4000
        self._result_name = ""
        self._rng = random.Random()
        self._blink_job = None
        self._big_display_job = None
        self._sort_reverse = False  # 排序方向（新增8）
        self._current_sort_col = ""

        self._setup_style()
        self._build_ui()
        self._apply_theme()

    # ----------------------------------------------------------
    # 音效 & 闪烁
    # ----------------------------------------------------------
    def _beep(self, freq, dur):
        if not self.sound_enabled or not _HAS_SOUND: return
        try: winsound.Beep(freq, dur)
        except Exception: pass

    def _play_start_sound(self): self._beep(400, 300)
    def _play_tick_sound(self):  self._beep(800, 40)
    def _play_stop_sound(self):  self._beep(1000, 500)

    def _start_blink(self):
        if not self.blink_enabled or self._blink_job: return
        def blink():
            try:
                current = self.name_label.cget("fg")
                new = "#FF4444" if current == "#FFFFFF" else "#FFFFFF"
                self.name_label.config(fg=new)
                self._blink_job = self.root.after(120, blink)
            except Exception: pass
        blink()

    def _stop_blink(self):
        if self._blink_job:
            self.root.after_cancel(self._blink_job)
            self._blink_job = None
        try: self.name_label.config(fg="#FFFFFF")
        except Exception: pass

    # ----------------------------------------------------------
    # 主题切换（新增15）
    # ----------------------------------------------------------
    def _apply_theme(self):
        if self.dark_theme:
            self.name_frame.config(bg="#1a1a2e")
            self.name_label.config(bg="#1a1a2e", fg="#FFFFFF")
            self.root.config(bg="#16213e")
        else:
            self.name_frame.config(bg="#f0f0f0")
            self.name_label.config(bg="#f0f0f0", fg="#000000")
            self.root.config(bg="#ffffff")

    def _toggle_theme(self):
        self.dark_theme = not self.dark_theme
        self._apply_theme()
        self._status("暗色主题" if self.dark_theme else "亮色主题")

    # ----------------------------------------------------------
    # 样式 & UI
    # ----------------------------------------------------------
    def _setup_style(self):
        style = ttk.Style()
        style.theme_use("clam")
        style.configure("TLabel", font=("Microsoft YaHei", 10))
        style.configure("TButton", font=("Microsoft YaHei", 10))
        style.configure("Treeview", font=("Microsoft YaHei", 9))
        style.configure("Treeview.Heading", font=("Microsoft YaHei", 10, "bold"))

    def _build_ui(self):
        menubar = tk.Menu(self.root)

        file_menu = tk.Menu(menubar, tearoff=0)
        file_menu.add_command(label="导入名单(TXT)", command=lambda: self._import_names("txt"))
        file_menu.add_command(label="导入名单(CSV)", command=lambda: self._import_names("csv"))
        file_menu.add_separator()
        file_menu.add_command(label="导出记录(CSV)", command=self._export_csv)
        menubar.add_cascade(label="文件", menu=file_menu)

        list_menu = tk.Menu(menubar, tearoff=0)
        list_menu.add_command(label="添加人员", command=self._add_person)
        list_menu.add_command(label="编辑选中", command=self._edit_person)
        list_menu.add_command(label="删除选中", command=self._delete_person)
        list_menu.add_separator()
        list_menu.add_command(label="重置已抽状态", command=self._reset_drawn)
        list_menu.add_separator()
        list_menu.add_command(label="随机分组", command=self._group_dialog)
        list_menu.add_command(label="查看分组", command=self._show_groups)
        menubar.add_cascade(label="名单", menu=list_menu)

        tool_menu = tk.Menu(menubar, tearoff=0)
        tool_menu.add_command(label="统计", command=self._show_stats)
        tool_menu.add_command(label="切换主题", command=self._toggle_theme)
        menubar.add_cascade(label="工具", menu=tool_menu)

        help_menu = tk.Menu(menubar, tearoff=0)
        help_menu.add_command(label="关于", command=self._about)
        menubar.add_cascade(label="帮助", menu=help_menu)
        self.root.config(menu=menubar)

        # 搜索栏（新增16）
        search_frame = ttk.Frame(self.root)
        search_frame.pack(fill=tk.X, padx=5, pady=(5,0))
        ttk.Label(search_frame, text="🔍 搜索:").pack(side=tk.LEFT, padx=(0,5))
        self.search_var = tk.StringVar()
        self.search_var.trace("w", self._on_search)
        ttk.Entry(search_frame, textvariable=self.search_var, width=30).pack(side=tk.LEFT, padx=5)
        ttk.Label(search_frame, text="(实时过滤名单)").pack(side=tk.LEFT, padx=5)

        main = ttk.PanedWindow(self.root, orient=tk.HORIZONTAL)
        main.pack(fill=tk.BOTH, expand=True, padx=5, pady=5)

        # 左侧：点名区
        left = ttk.Frame(main)
        main.add(left, weight=3)

        self.name_frame = tk.Frame(left, bg="#1a1a2e", height=260)
        self.name_frame.pack(fill=tk.BOTH, expand=True, pady=10)
        self.name_frame.pack_propagate(False)
        self.name_label = tk.Label(self.name_frame, text="准备就绪", bg="#1a1a2e",
                                   fg="#FFFFFF", font=("Microsoft YaHei", 48, "bold"))
        self.name_label.pack(expand=True)

        ctrl = ttk.Frame(left)
        ctrl.pack(pady=10)

        self.draw_btn = ttk.Button(ctrl, text="🎯 开始点名 (F9)", command=self._start_draw)
        self.draw_btn.pack(side=tk.LEFT, padx=5)
        self.stop_btn = ttk.Button(ctrl, text="⏹ 停止 (F10)", command=self._stop_anim, state=tk.DISABLED)
        self.stop_btn.pack(side=tk.LEFT, padx=5)
        ttk.Button(ctrl, text="🔄 重置 (F11)", command=self._reset_drawn).pack(side=tk.LEFT, padx=5)

        ttk.Checkbutton(ctrl, text="未抽到的优先", variable=self.priority_mode).pack(side=tk.LEFT, padx=10)
        ttk.Checkbutton(ctrl, text="允许重复", variable=self.allow_repeat).pack(side=tk.LEFT, padx=10)

        ttk.Label(ctrl, text="动画时长(ms):").pack(side=tk.LEFT, padx=(15,2))
        self.anim_var = tk.IntVar(value=4000)
        ttk.Spinbox(ctrl, from_=1000, to=15000, textvariable=self.anim_var, width=7).pack(side=tk.LEFT)

        # 右侧
        right = ttk.Frame(main)
        main.add(right, weight=2)
        tabs = ttk.Notebook(right)
        tabs.pack(fill=tk.BOTH, expand=True)

        # 名单（带排序，新增8）
        list_tab = ttk.Frame(tabs)
        tabs.add(list_tab, text="名单")
        self.tree = ttk.Treeview(list_tab, columns=("name","weight","drawn","group"),
                                  show="headings", height=15)
        col_cfgs = [("name","姓名",150),("weight","权重",70),("drawn","已抽",70),("group","分组",100)]
        for col, txt, w in col_cfgs:
            self.tree.heading(col, text=txt, command=lambda c=col: self._sort_by(c))
            self.tree.column(col, width=w)
        self.tree.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)
        ttk.Scrollbar(list_tab, orient=tk.VERTICAL, command=self.tree.yview).pack(side=tk.RIGHT, fill=tk.Y)
        self.tree.configure(yscrollcommand=self.tree.yview)

        tree_btn = ttk.Frame(list_tab)
        tree_btn.pack(fill=tk.X, pady=2)
        ttk.Button(tree_btn, text="➕ 添加", command=self._add_person).pack(side=tk.LEFT, padx=2)
        ttk.Button(tree_btn, text="✏️ 编辑", command=self._edit_person).pack(side=tk.LEFT, padx=2)
        ttk.Button(tree_btn, text="🗑️ 删除", command=self._delete_person).pack(side=tk.LEFT, padx=2)
        ttk.Button(tree_btn, text="🔄 重置已抽", command=self._reset_drawn).pack(side=tk.LEFT, padx=2)

        # 记录（支持点击删除单条，新增12）
        hist_tab = ttk.Frame(tabs)
        tabs.add(hist_tab, text="点名记录")
        self.hist_text = tk.Text(hist_tab, height=20, wrap=tk.WORD, font=("Consolas", 9))
        self.hist_text.pack(fill=tk.BOTH, expand=True, side=tk.LEFT)
        self.hist_text.bind("<Button-3>", self._on_hist_right_click)
        ttk.Scrollbar(hist_tab, orient=tk.VERTICAL, command=self.hist_text.yview).pack(side=tk.RIGHT, fill=tk.Y)
        self.hist_text.configure(yscrollcommand=self.hist_text.yview)

        # 统计
        stat_tab = ttk.Frame(tabs)
        tabs.add(stat_tab, text="统计")
        self.stat_text = tk.Text(stat_tab, height=20, wrap=tk.WORD, font=("Consolas", 10))
        self.stat_text.pack(fill=tk.BOTH, expand=True)

        # 设置
        set_tab = ttk.Frame(tabs)
        tabs.add(set_tab, text="设置")
        si = ttk.Frame(set_tab); si.pack(pady=20)
        self.sound_var = tk.BooleanVar(value=self.sound_enabled)
        self.blink_var = tk.BooleanVar(value=self.blink_enabled)
        ttk.Checkbutton(si, text="音效", variable=self.sound_var,
                        command=lambda: setattr(self, 'sound_enabled', self.sound_var.get())).grid(row=0, column=0, sticky=tk.W)
        ttk.Checkbutton(si, text="闪烁", variable=self.blink_var,
                        command=lambda: setattr(self, 'blink_enabled', self.blink_var.get())).grid(row=1, column=0, sticky=tk.W)
        ttk.Button(si, text="切换暗色/亮色主题", command=self._toggle_theme).grid(row=2, column=0, sticky=tk.W, pady=10)

        self.status_bar = ttk.Label(self.root, text="就绪 | F9开始 F10停止 F11重置 | 右键记录可删除",
                                    relief=tk.SUNKEN, anchor=tk.W)
        self.status_bar.pack(side=tk.BOTTOM, fill=tk.X)

        # 快捷键（新增9）
        self.root.bind("<F9>", lambda e: self._start_draw())
        self.root.bind("<F10>", lambda e: self._stop_anim())
        self.root.bind("<F11>", lambda e: self._reset_drawn())

        # 拖拽文件导入（新增14）
        self.root.drop_target_register = getattr(self.root, 'drop_target_register', None)
        # Windows 拖拽用 WM_DROPFILES
        self._enable_drop()

    # ----------------------------------------------------------
    # 拖拽支持（新增14，Windows）
    # ----------------------------------------------------------
    def _enable_drop(self):
        """尝试注册拖拽，失败则静默"""
        try:
            import ctypes
            from ctypes import wintypes
            ctypes.windll.shell32.DragAcceptFiles(self.root.winfo_id(), True)
            self.root.bind("<WM_DROPFILES>", self._on_drop)
        except Exception:
            pass

    def _on_drop(self, event):
        try:
            import ctypes
            hdrop = event.data
            # 简化版：从命令行参数获取拖拽文件
            files = self.root.tk.splitlist(event.data)
            for f in files:
                if f.lower().endswith('.txt'):
                    self.pool = NamePool.from_txt(f)
                elif f.lower().endswith('.csv'):
                    self.pool = NamePool.from_csv(f)
            self._refresh_list()
            self._status(f"通过拖拽导入了 {len(self.pool.people)} 人")
        except Exception:
            pass

    # ----------------------------------------------------------
    # 搜索过滤（新增16）
    # ----------------------------------------------------------
    def _on_search(self, *args):
        self._refresh_list()

    # ----------------------------------------------------------
    # 排序（新增8）
    # ----------------------------------------------------------
    def _sort_by(self, col):
        if self._current_sort_col == col:
            self._sort_reverse = not self._sort_reverse
        else:
            self._sort_reverse = False
            self._current_sort_col = col

        key_map = {
            "name": lambda p: p.name,
            "weight": lambda p: p.weight,
            "drawn": lambda p: p.drawn,
            "group": lambda p: p.group,
        }
        if col in key_map:
            self.pool.people.sort(key=key_map[col], reverse=self._sort_reverse)
        self._refresh_list()
        self._status(f"按「{col}」{'降序' if self._sort_reverse else '升序'}排序")

    # ----------------------------------------------------------
    # 核心：抽取
    # ----------------------------------------------------------
    def _pick_one(self) -> Optional[Person]:
        if self.allow_repeat.get():
            candidates = list(self.pool.people)
        else:
            candidates = self.pool.available()

        if not candidates:
            return None

        if self.priority_mode.get() and not self.allow_repeat.get():
            weights = [p.weight * 100 if not p.drawn else p.weight for p in candidates]
        else:
            weights = [p.weight for p in candidates]

        total = sum(weights)
        if total <= 0:
            return self._rng.choice(candidates)
        r = self._rng.uniform(0, total)
        cum = 0.0
        for p, w in zip(candidates, weights):
            cum += w
            if r <= cum:
                return p
        return candidates[-1]

    # ----------------------------------------------------------
    # 动画
    # ----------------------------------------------------------
    def _start_draw(self):
        if self._animating:
            return
        if not self.pool.people:
            messagebox.showwarning("警告", "名单为空，请先导入！")
            return

        result = self._pick_one()
        if not result:
            messagebox.showinfo("提示", "所有人都已抽过一轮，已自动重置")
            self.pool.reset_drawn()
            result = self._pick_one()
        if not result:
            return

        self._result_name = result.name
        self._anim_duration = self.anim_var.get()
        self._anim_start = time.time() * 1000
        self._animating = True
        self.draw_btn.config(state=tk.DISABLED)
        self.stop_btn.config(state=tk.NORMAL)
        self.name_label.config(text="...", fg="#FFFFFF", font=("Microsoft YaHei", 48, "bold"))
        self._play_start_sound()
        self._roll_frame()

    def _roll_frame(self):
        if not self._animating: return
        elapsed = time.time() * 1000 - self._anim_start
        p = min(elapsed / self._anim_duration, 1.0)
        eased = 1 - (1 - p) ** 4

        if p < 1.0:
            pool = self.pool.people if self.allow_repeat.get() else (self.pool.available() or self.pool.people)
            self.name_label.config(text=self._rng.choice(pool).name)
            if p > 0.8 and self.blink_enabled and not self._blink_job:
                self._start_blink()
            if p > 0.95:
                self._play_tick_sound()
            interval = int(16 + eased * 80)
            self.root.after(interval, self._roll_frame)
        else:
            self._animating = False
            self._stop_blink()
            self._play_stop_sound()
            self._finish_draw()

    def _stop_anim(self):
        if self._animating:
            self._animating = False
            self._stop_blink()
            self._play_stop_sound()
            self._finish_draw()

    def _finish_draw(self):
        self.draw_btn.config(state=tk.NORMAL)
        self.stop_btn.config(state=tk.DISABLED)

        result = None
        for p in self.pool.people:
            if p.name == self._result_name and (self.allow_repeat.get() or not p.drawn):
                result = p
                break
        if not result:
            for p in self.pool.people:
                if p.name == self._result_name:
                    result = p
                    break
        if not result:
            return

        if not self.allow_repeat.get():
            result.drawn = True

        # 大号显示结果（新增10）
        self.name_label.config(text=self._result_name, fg="#FFD700",
                               font=("Microsoft YaHei", 72, "bold"))
        self._big_display_job = self.root.after(3000, self._shrink_display)

        rec = DrawRecord(name=self._result_name, timestamp=datetime.now().isoformat(),
                         mode="加权" if self.priority_mode.get() else "等概率")
        self.history.append(rec)
        self._append_history(rec)
        self._refresh_list()

    def _shrink_display(self):
        """3秒后缩小回正常大小（新增10）"""
        self.name_label.config(text="准备就绪", fg="#FFFFFF",
                               font=("Microsoft YaHei", 48, "bold"))
        self._big_display_job = None

    # ----------------------------------------------------------
    # 名单操作
    # ----------------------------------------------------------
    def _import_names(self, fmt):
        if fmt == "txt":
            path = filedialog.askopenfilename(filetypes=[("文本文件","*.txt")])
            if path:
                old_count = len(self.pool.people)
                new_pool = NamePool.from_txt(path)
                added = 0
                for p in new_pool.people:
                    if self.pool.add(p):
                        added += 1
        else:
            path = filedialog.askopenfilename(filetypes=[("CSV","*.csv")])
            if path:
                old_count = len(self.pool.people)
                new_pool = NamePool.from_csv(path)
                added = 0
                for p in new_pool.people:
                    if self.pool.add(p):
                        added += 1
        self._refresh_list()
        self._status(f"导入完成，新增 {added} 人（已自动去重）")

    def _add_person(self):
        dlg = PersonDialog(self.root, title="添加人员")
        if dlg.result:
            if self.pool.add(dlg.result):
                self._refresh_list()
            else:
                messagebox.showwarning("警告", f"「{dlg.result.name}」已存在，未重复添加")

    def _edit_person(self):
        sel = self.tree.selection()
        if not sel: return
        idx = self.tree.index(sel[0])
        dlg = PersonDialog(self.root, person=self.pool.people[idx], title="编辑人员")
        if dlg.result:
            self.pool.people[idx] = dlg.result
            self._refresh_list()

    def _delete_person(self):
        sel = self.tree.selection()
        if not sel: return
        idx = self.tree.index(sel[0])
        if messagebox.askyesno("确认", f"删除「{self.pool.people[idx].name}」？"):
            self.pool.people.pop(idx)
            self._refresh_list()

    def _reset_drawn(self):
        self.pool.reset_drawn()
        self._refresh_list()
        self._status("已重置所有人的已抽状态")

    # ----------------------------------------------------------
    # 分组
    # ----------------------------------------------------------
    def _group_dialog(self):
        if not self.pool.people:
            messagebox.showinfo("提示", "名单为空")
            return
        n = simpledialog.askinteger("随机分组", "分成几组:", minvalue=2,
                                    maxvalue=len(self.pool.people), initialvalue=2)
        if not n: return
        self._group(n)
        self._refresh_list()
        self._show_groups()

    def _group(self, n: int):
        people = list(self.pool.people)
        self._rng.shuffle(people)
        for i, p in enumerate(people):
            p.group = f"第{i % n + 1}组"
        by_name = {p.name: p.group for p in people}
        for p in self.pool.people:
            p.group = by_name.get(p.name, "")

    def _show_groups(self):
        if not self.pool.people:
            return
        groups = {}
        for p in self.pool.people:
            g = p.group or "未分组"
            groups.setdefault(g, []).append(p.name)
        win = tk.Toplevel(self.root)
        win.title("分组结果")
        win.geometry("500x500")
        text = tk.Text(win, wrap=tk.WORD, font=("Microsoft YaHei", 11))
        text.pack(fill=tk.BOTH, expand=True)
        for g in sorted(groups.keys()):
            text.insert(tk.END, f"【{g}】({len(groups[g])}人)\n")
            for name in groups[g]:
                text.insert(tk.END, f"  • {name}\n")
            text.insert(tk.END, "\n")

    # ----------------------------------------------------------
    # 统计 & 导出
    # ----------------------------------------------------------
    def _show_stats(self):
        self.stat_text.delete("1.0", tk.END)
        if not self.history:
            self.stat_text.insert(tk.END, "暂无记录\n")
            return
        counter = {}
        for r in self.history:
            counter[r.name] = counter.get(r.name, 0) + 1
        total = len(self.history)
        self.stat_text.insert(tk.END, f"总点名次数: {total}\n")
        self.stat_text.insert(tk.END, f"参与人数: {len(counter)}\n\n")
        for name, cnt in sorted(counter.items(), key=lambda x: x[1], reverse=True):
            pct = cnt / total * 100
            bar = "█" * int(pct / 2)
            self.stat_text.insert(tk.END, f"{name:15s} {cnt:3d}次 {pct:5.1f}% {bar}\n")

    def _export_csv(self):
        if not self.history:
            messagebox.showinfo("提示", "暂无记录")
            return
        path = filedialog.asksaveasfilename(defaultextension=".csv")
        if path:
            with open(path, "w", newline="", encoding="utf-8-sig") as f:
                w = csv.DictWriter(f, fieldnames=["name","timestamp","mode"])
                w.writeheader()
                for r in self.history:
                    w.writerow({"name":r.name,"timestamp":r.timestamp,"mode":r.mode})

    # ----------------------------------------------------------
    # 历史右键删除（新增12）
    # ----------------------------------------------------------
    def _on_hist_right_click(self, event):
        menu = tk.Menu(self.root, tearoff=0)
        menu.add_command(label="删除最近一条", command=self._delete_last_history)
        menu.add_command(label="清空全部记录", command=self._clear_history)
        menu.tk_popup(event.x_root, event.y_root)

    def _delete_last_history(self):
        if self.history:
            self.history.pop()
            self.hist_text.delete("1.0", tk.END)
            for r in self.history:
                self._append_history(r)

    def _clear_history(self):
        if messagebox.askyesno("确认", "清空所有记录？"):
            self.history.clear()
            self.hist_text.delete("1.0", tk.END)

    # ----------------------------------------------------------
    # 刷新（含搜索过滤 + √显示，新增7/16）
    # ----------------------------------------------------------
    def _refresh_list(self):
        for item in self.tree.get_children():
            self.tree.delete(item)
        kw = self.search_var.get().strip()
        if kw:
            filtered = self.pool.search(kw)
        else:
            filtered = self.pool.people
        for p in filtered:
            self.tree.insert("", tk.END, values=(
                p.name, f"{p.weight:.1f}",
                "√" if p.drawn else "",   # 新增7：已抽显示√
                p.group or ""
            ))

    def _append_history(self, record: DrawRecord):
        ts = record.timestamp[11:19] if len(record.timestamp) > 19 else ""
        line = f"[{ts}] {record.name} ({record.mode})\n"
        self.hist_text.insert(tk.END, line)
        self.hist_text.see(tk.END)

    # ----------------------------------------------------------
    # 工具
    # ----------------------------------------------------------
    def _status(self, msg: str):
        self.status_bar.config(text=msg)

    def _about(self):
        messagebox.showinfo("关于",
            "🎯 随机点名器 v2.0\n\n"
            "新增功能：\n"
            "• 已抽列显示 √\n"
            "• 名单列表点击列头排序\n"
            "• 快捷键 F9/F10/F11\n"
            "• 结果大号弹显3秒\n"
            "• 批量导入自动去重\n"
            "• 历史右键删除\n"
            "• 统计文字柱状图\n"
            "• 拖拽TXT导入\n"
            "• 暗色/亮色主题切换\n"
            "• 名单搜索过滤\n"
            "· ☒是选择\n© 2026")


# ============================================================
# 人员编辑对话框
# ============================================================

class PersonDialog(tk.Toplevel):
    def __init__(self, parent, person=None, title="人员"):
        super().__init__(parent)
        self.title(title); self.geometry("320x200")
        self.transient(parent); self.grab_set(); self.result = None
        self._build(person); self.wait_window()

    def _build(self, person):
        f = ttk.Frame(self, padding=10); f.pack(fill=tk.BOTH, expand=True)
        self.name_var = tk.StringVar(value=person.name if person else "")
        self.weight_var = tk.DoubleVar(value=person.weight if person else 1.0)
        self.group_var = tk.StringVar(value=person.group if person else "")

        ttk.Label(f, text="姓名:").grid(row=0, column=0, sticky=tk.W, pady=5)
        ttk.Entry(f, textvariable=self.name_var, width=20).grid(row=0, column=1, pady=5)
        ttk.Label(f, text="权重:").grid(row=1, column=0, sticky=tk.W, pady=5)
        ttk.Spinbox(f, from_=0.1, to=999, increment=0.1, textvariable=self.weight_var, width=10).grid(row=1, column=1, pady=5)
        ttk.Label(f, text="分组:").grid(row=2, column=0, sticky=tk.W, pady=5)
        ttk.Entry(f, textvariable=self.group_var, width=20).grid(row=2, column=1, pady=5)

        btns = ttk.Frame(f); btns.grid(row=3, column=0, columnspan=2, pady=15)
        ttk.Button(btns, text="确定", command=self._ok).pack(side=tk.LEFT, padx=10)
        ttk.Button(btns, text="取消", command=self.destroy).pack(side=tk.LEFT)

    def _ok(self):
        name = self.name_var.get().strip()
        if not name:
            messagebox.showwarning("警告", "姓名不能为空"); return
        self.result = Person(name=name, weight=self.weight_var.get(), group=self.group_var.get())
        self.destroy()


# ============================================================
# 入口
# ============================================================

def main():
    root = tk.Tk()
    app = NamePickerApp(root)
    root.mainloop()

if __name__ == "__main__":
    main()