"""
配色练习 · Color Match Challenge
===========================
游戏化闯关式色轮搭配练习工具
- 6 大关卡：互补色 → 类似色 → 三角色 → 分裂互补 → 矩形色 → 自由创作
- 每关随机出题，限时作答，积分排行
- 基于 tkinter + Canvas 绘制色轮
- 单文件，Python 3.7+ 即可运行
"""

import tkinter as tk
from tkinter import ttk, messagebox, font as tkfont
import math
import random
import colorsys
import json
import os
from datetime import datetime
from typing import List, Tuple, Optional


# ══════════════════════════════════════════════
# 第一部分：颜色工具函数
# ══════════════════════════════════════════════

def hsl_to_rgb(h: float, s: float, l: float) -> Tuple[int, int, int]:
    """HSL → RGB（h/s/l 均为 0~1）"""
    r, g, b = colorsys.hls_to_rgb(h, l, s)
    return (round(r * 255), round(g * 255), round(b * 255))


def rgb_to_hex(r: int, g: int, b: int) -> str:
    """RGB → HEX"""
    return f"#{r:02x}{g:02x}{b:02x}"


def hex_to_rgb(hex_str: str) -> Tuple[int, int, int]:
    """HEX → RGB"""
    h = hex_str.lstrip("#")
    return tuple(int(h[i:i+2], 16) for i in (0, 2, 4))


def hue_to_rgb(hue: float) -> Tuple[int, int, int]:
    """色相值（0~360）→ RGB（全饱和、中亮度）"""
    return hsl_to_rgb(hue / 360, 0.8, 0.55)


def color_distance(c1: Tuple[int, int, int], c2: Tuple[int, int, int]) -> float:
    """颜色距离（加权欧氏距离）"""
    r_mean = (c1[0] + c2[0]) / 2
    dr = c1[0] - c2[0]
    dg = c1[1] - c2[1]
    db = c1[2] - c2[2]
    return math.sqrt(
        (2 + r_mean / 256) * dr * dr +
        4 * dg * dg +
        (2 + (255 - r_mean) / 256) * db * db
    )


def hue_distance(h1: float, h2: float) -> float:
    """色相环最短距离（角度）"""
    d = abs(h1 - h2) % 360
    return min(d, 360 - d)


# ══════════════════════════════════════════════
# 第二部分：关卡规则引擎
# ══════════════════════════════════════════════

class Level:
    """关卡配置"""
    
    def __init__(self, level_id: int, name: str, desc: str, hint: str,
                 rule_func, select_count: int, time_limit: int,
                 base_score: int):
        self.level_id = level_id
        self.name = name
        self.desc = desc
        self.hint = hint
        self.rule_func = rule_func
        self.select_count = select_count
        self.time_limit = time_limit
        self.base_score = base_score


def complementary_rule(base_hue: float, selected: List[float]) -> Tuple[bool, str]:
    """互补色规则：选中的色相与基准色相差 180° ± 15°"""
    target = (base_hue + 180) % 360
    if len(selected) != 1:
        return False, f"需要选择 1 个颜色，你选了 {len(selected)} 个"
    diff = hue_distance(selected[0], target)
    if diff <= 15:
        return True, f"✅ 正确！{selected[0]:.0f}° 与目标 {target:.0f}° 相差 {diff:.0f}°（≤15°）"
    else:
        return False, f"❌ 相差 {diff:.0f}°，应在 15° 以内（目标色相 {target:.0f}°）"


def analogous_rule(base_hue: float, selected: List[float]) -> Tuple[bool, str]:
    """类似色规则：选中的色相与基准色相差 ±30°"""
    if len(selected) != 1:
        return False, f"需要选择 1 个颜色，你选了 {len(selected)} 个"
    diff = hue_distance(selected[0], base_hue)
    if diff <= 30 and diff >= 5:
        return True, f"✅ 正确！相差 {diff:.0f}°（5°~30°）"
    elif diff < 5:
        return False, f"❌ 太近了，相差只有 {diff:.0f}°（应 ≥5°）"
    else:
        return False, f"❌ 相差 {diff:.0f}°，应在 5°~30° 之间"


def triadic_rule(base_hue: float, selected: List[float]) -> Tuple[bool, str]:
    """三角色规则：选中的两色分别与基准色相差 +120° 和 -120°"""
    targets = [(base_hue + 120) % 360, (base_hue - 120) % 360]
    if len(selected) != 2:
        return False, f"需要选择 2 个颜色，你选了 {len(selected)} 个"
    
    matched = 0
    details = []
    for s in selected:
        best_diff = min(hue_distance(s, t) for t in targets)
        if best_diff <= 20:
            matched += 1
            details.append(f"{s:.0f}°（距目标 {best_diff:.0f}°）")
        else:
            details.append(f"{s:.0f}°（偏离 {best_diff:.0f}°）")
    
    if matched == 2:
        return True, f"✅ 正确！{', '.join(details)}"
    else:
        return False, f"❌ 匹配了 {matched}/2 个：{', '.join(details)}"


def split_complementary_rule(base_hue: float, selected: List[float]) -> Tuple[bool, str]:
    """分裂互补规则：互补色左右各30°的两色"""
    comp = (base_hue + 180) % 360
    targets = [(comp - 30) % 360, (comp + 30) % 360]
    
    if len(selected) != 2:
        return False, f"需要选择 2 个颜色，你选了 {len(selected)} 个"
    
    matched = 0
    details = []
    for s in selected:
        best_diff = min(hue_distance(s, t) for t in targets)
        if best_diff <= 15:
            matched += 1
            details.append(f"{s:.0f}°（距目标 {best_diff:.0f}°）")
        else:
            details.append(f"{s:.0f}°（偏离 {best_diff:.0f}°）")
    
    if matched == 2:
        return True, f"✅ 正确！{', '.join(details)}"
    else:
        return False, f"❌ 匹配了 {matched}/2 个：{', '.join(details)}"


def tetradic_rule(base_hue: float, selected: List[float]) -> Tuple[bool, str]:
    """矩形色规则：两组互补色（相距60°）"""
    targets = [
        (base_hue + 180) % 360,
        (base_hue + 60) % 360,
        (base_hue + 240) % 360,
    ]
    
    if len(selected) != 3:
        return False, f"需要选择 3 个颜色，你选了 {len(selected)} 个"
    
    matched = 0
    details = []
    used_targets = set()
    
    for s in selected:
        best_idx = -1
        best_diff = 999
        for i, t in enumerate(targets):
            if i in used_targets:
                continue
            diff = hue_distance(s, t)
            if diff < best_diff:
                best_diff = diff
                best_idx = i
        
        if best_idx >= 0 and best_diff <= 20:
            matched += 1
            used_targets.add(best_idx)
            details.append(f"{s:.0f}°（距目标 {best_diff:.0f}°）")
        else:
            details.append(f"{s:.0f}°（偏离 {best_diff:.0f}°）")
    
    if matched == 3:
        return True, f"✅ 正确！{', '.join(details)}"
    else:
        return False, f"❌ 匹配了 {matched}/3 个：{', '.join(details)}"


def free_style_rule(base_hue: float, selected: List[float]) -> Tuple[bool, str]:
    """自由创作规则：按色相均匀度评分"""
    if len(selected) < 2:
        return False, "至少选择 2 个颜色才能评分"
    
    # 计算色相均匀度
    sorted_hues = sorted(selected)
    gaps = []
    for i in range(len(sorted_hues)):
        next_i = (i + 1) % len(sorted_hues)
        gap = (sorted_hues[next_i] - sorted_hues[i]) % 360
        gaps.append(gap)
    
    ideal_gap = 360 / len(sorted_hues)
    deviation = sum(abs(g - ideal_gap) for g in gaps) / len(gaps)
    
    # 均匀度评分（0~100）
    uniformity = max(0, 100 - deviation * 2)
    
    if uniformity >= 70:
        return True, f"✅ 配色均匀度 {uniformity:.0f}分！色相分布很均衡"
    elif uniformity >= 50:
        return True, f"✅ 还可以，均匀度 {uniformity:.0f}分"
    else:
        return False, f"❌ 均匀度 {uniformity:.0f}分，色相分布不够均匀（理想差距 {ideal_gap:.0f}°）"


# ══════════════════════════════════════════════
# 第三部分：关卡配置
# ══════════════════════════════════════════════

LEVELS = [
    Level(0, "互补色", "找基准色的对面色（相差180°）",
          "选择与基准色色相相差180°的颜色",
          complementary_rule, 1, 20, 100),
    Level(1, "类似色", "找相邻颜色（±30°范围内）",
          "选择与基准色色相相差5°~30°的颜色",
          analogous_rule, 1, 20, 150),
    Level(2, "三角色", "找构成等边三角形的两色（±120°）",
          "选择两个与基准色分别相差+120°和-120°的颜色",
          triadic_rule, 2, 30, 200),
    Level(3, "分裂互补", "互补色两侧各30°的两色",
          "选择互补色左右各30°的两个颜色",
          split_complementary_rule, 2, 30, 250),
    Level(4, "矩形色", "两组互补色（相距60°）",
          "选择三个颜色：互补色及其偏移60°后的颜色",
          tetradic_rule, 3, 40, 300),
    Level(5, "自由创作", "任选颜色，按均匀度评分",
          "选择任意颜色组合，系统将根据色相均匀度评分",
          free_style_rule, 3, 60, 350),
]


# ══════════════════════════════════════════════
# 第四部分：排行榜
# ══════════════════════════════════════════════

RANKING_FILE = "color_match_ranking.json"


def load_rankings() -> List[dict]:
    """加载排行榜"""
    if not os.path.exists(RANKING_FILE):
        return []
    try:
        with open(RANKING_FILE, "r", encoding="utf-8") as f:
            data = json.load(f)
            return sorted(data, key=lambda x: x["score"], reverse=True)[:10]
    except (json.JSONDecodeError, KeyError):
        return []


def save_ranking(entry: dict):
    """保存一条新纪录"""
    rankings = load_rankings()
    rankings.append(entry)
    rankings = sorted(rankings, key=lambda x: x["score"], reverse=True)[:10]
    try:
        with open(RANKING_FILE, "w", encoding="utf-8") as f:
            json.dump(rankings, f, ensure_ascii=False, indent=2)
    except IOError:
        pass


# ══════════════════════════════════════════════
# 第五部分：GUI 主程序
# ══════════════════════════════════════════════

class ColorMatchGame:
    """配色练习主程序"""
    
    def __init__(self, root: tk.Tk):
        self.root = root
        root.title("🎨 配色练习 · Color Match Challenge")
        root.geometry("950x700")
        root.minsize(880, 620)
        
        # 游戏状态
        self.current_level = 0
        self.selected_hues: List[float] = []
        self.base_hue: float = 0
        self.score = 0
        self.combo = 0
        self.max_combo = 0
        self.total_attempts = 0
        self.correct_attempts = 0
        self.time_left = 0
        self.timer_id: Optional[str] = None
        self.is_running = False
        self.game_over = False
        
        # 色轮参数
        self.wheel_center_x = 250
        self.wheel_center_y = 290
        self.wheel_radius = 200
        self.inner_radius = 40
        
        # 构建界面
        self._build_ui()
        self._start_level(0)
    
    # ────────────── 界面构建 ──────────────
    
    def _build_ui(self):
        """构建完整界面"""
        # 顶部标题栏
        header = tk.Frame(self.root, bg="#2c3e50", height=50)
        header.pack(fill="x")
        header.pack_propagate(False)
        
        tk.Label(header, text="🎨 配色练习", font=("Microsoft YaHei", 16, "bold"),
                 bg="#2c3e50", fg="white").pack(side="left", padx=20, pady=8)
        
        self.level_label = tk.Label(header, text="第1关 · 互补色", 
                                     font=("Microsoft YaHei", 11),
                                     bg="#2c3e50", fg="#ecf0f1")
        self.level_label.pack(side="left", padx=10)
        
        self.score_label = tk.Label(header, text="⭐ 0分", 
                                     font=("Microsoft YaHei", 11, "bold"),
                                     bg="#2c3e50", fg="#f1c40f")
        self.score_label.pack(side="right", padx=15)
        
        self.combo_label = tk.Label(header, text="🔥 0", 
                                     font=("Microsoft YaHei", 11),
                                     bg="#2c3e50", fg="#e74c3c")
        self.combo_label.pack(side="right", padx=10)
        
        self.timer_label = tk.Label(header, text="⏱ 00:00", 
                                     font=("Consolas", 12, "bold"),
                                     bg="#2c3e50", fg="#3498db")
        self.timer_label.pack(side="right", padx=15)
        
        # 主体区域
        main = tk.Frame(self.root)
        main.pack(fill="both", expand=True, padx=10, pady=5)
        
        # 左：色轮
        left = tk.Frame(main, width=500)
        left.pack(side="left", fill="both", expand=True)
        left.pack_propagate(False)
        
        self.wheel_canvas = tk.Canvas(left, width=500, height=500, 
                                       bg="#f8f9fa", highlightthickness=0)
        self.wheel_canvas.pack(fill="both", expand=True)
        self.wheel_canvas.bind("<Button-1>", self._on_wheel_click)
        self.wheel_canvas.bind("<Motion>", self._on_wheel_hover)
        
        # 底部色块显示
        swatch_frame = tk.Frame(left)
        swatch_frame.pack(fill="x", pady=2)
        
        self.swatch_canvases = []
        for i in range(3):
            c = tk.Canvas(swatch_frame, width=60, height=40, 
                          bg="#ddd", highlightthickness=1, 
                          highlightbackground="#aaa")
            c.pack(side="left", padx=5)
            self.swatch_canvases.append(c)
        
        # 右：题目和操作
        right = tk.Frame(main, width=400)
        right.pack(side="right", fill="both", expand=True)
        right.pack_propagate(False)
        
        # 题目描述
        question_frame = tk.Frame(right, bg="#eef2f7", padx=15, pady=10)
        question_frame.pack(fill="x", pady=5)
        
        tk.Label(question_frame, text="📋 题目", 
                 font=("Microsoft YaHei", 12, "bold"),
                 bg="#eef2f7", fg="#2c3e50").pack(anchor="w")
        
        self.question_var = tk.StringVar()
        tk.Label(question_frame, textvariable=self.question_var,
                 font=("Microsoft YaHei", 10), bg="#eef2f7", fg="#34495e",
                 wraplength=350, justify="left").pack(anchor="w", pady=5)
        
        # 基准色显示
        base_frame = tk.Frame(right, padx=15, pady=5)
        base_frame.pack(fill="x")
        
        tk.Label(base_frame, text="🎯 基准色", 
                 font=("Microsoft YaHei", 10, "bold")).pack(anchor="w")
        
        self.base_canvas = tk.Canvas(base_frame, width=80, height=40,
                                      bg="#eee", highlightthickness=1,
                                      highlightbackground="#aaa")
        self.base_canvas.pack(anchor="w", pady=3)
        
        self.base_info = tk.StringVar(value="等待生成...")
        tk.Label(base_frame, textvariable=self.base_info,
                 font=("Consolas", 9), fg="#555").pack(anchor="w")
        
        # 反馈区
        feedback_frame = tk.Frame(right, padx=15, pady=5)
        feedback_frame.pack(fill="x")
        
        tk.Label(feedback_frame, text="📝 反馈", 
                 font=("Microsoft YaHei", 10, "bold")).pack(anchor="w")
        
        self.feedback_var = tk.StringVar(value="等待作答...")
        tk.Label(feedback_frame, textvariable=self.feedback_var,
                 font=("Microsoft YaHei", 9), fg="#7f8c8d",
                 wraplength=350, justify="left").pack(anchor="w", pady=3)
        
        # 操作按钮
        btn_frame = tk.Frame(right, padx=15, pady=10)
        btn_frame.pack(fill="x")
        
        tk.Button(btn_frame, text="✅ 提交答案", command=self._submit_answer,
                  bg="#27ae60", fg="white", font=("Microsoft YaHei", 10),
                  padx=15, pady=5).pack(side="left", padx=3)
        
        tk.Button(btn_frame, text="🗑 清空选择", command=self._clear_selection,
                  bg="#e74c3c", fg="white", font=("Microsoft YaHei", 10),
                  padx=10, pady=5).pack(side="left", padx=3)
        
        tk.Button(btn_frame, text="💡 提示", command=self._show_hint,
                  bg="#3498db", fg="white", font=("Microsoft YaHei", 10),
                  padx=10, pady=5).pack(side="left", padx=3)
        
        # 进度
        progress_frame = tk.Frame(right, padx=15, pady=5)
        progress_frame.pack(fill="x")
        
        tk.Label(progress_frame, text="🏆 总进度", 
                 font=("Microsoft YaHei", 10, "bold")).pack(anchor="w")
        
        self.progress_var = tk.StringVar(value="0/6 关")
        tk.Label(progress_frame, textvariable=self.progress_var,
                 font=("Microsoft YaHei", 10), fg="#555").pack(anchor="w", pady=3)
        
        # 排行榜按钮
        tk.Button(right, text="📊 查看排行榜", command=self._show_ranking,
                  bg="#9b59b6", fg="white", font=("Microsoft YaHei", 10),
                  padx=15, pady=5).pack(padx=15, pady=10, anchor="w")
        
        # 底部状态栏
        status = tk.Frame(self.root, bg="#ecf0f1", height=30)
        status.pack(fill="x", side="bottom")
        status.pack_propagate(False)
        
        self.status_var = tk.StringVar(value="欢迎来到配色练习！点击色轮选择颜色，提交答案闯关")
        tk.Label(status, textvariable=self.status_var, font=("Microsoft YaHei", 9),
                 bg="#ecf0f1", fg="#7f8c8d").pack(side="left", padx=15, pady=5)
        
        # 键盘快捷键
        self.root.bind("<Return>", lambda e: self._submit_answer())
        self.root.bind("<Escape>", lambda e: self._clear_selection())
        
        # 初始绘制
        self._draw_wheel()
    
    def _draw_wheel(self):
        """绘制色轮"""
        self.wheel_canvas.delete("wheel")
        cx, cy = self.wheel_center_x, self.wheel_center_y
        r = self.wheel_radius
        
        # 绘制 360 个小扇形
        segments = 360
        for i in range(segments):
            angle = i * 360 / segments
            rad1 = math.radians(angle - 90)
            rad2 = math.radians(angle + 360 / segments - 90)
            
            x1 = cx + self.inner_radius * math.cos(rad1)
            y1 = cy + self.inner_radius * math.sin(rad1)
            x2 = cx + r * math.cos(rad1)
            y2 = cy + r * math.sin(rad1)
            x3 = cx + r * math.cos(rad2)
            y3 = cy + r * math.sin(rad2)
            x4 = cx + self.inner_radius * math.cos(rad2)
            y4 = cy + self.inner_radius * math.sin(rad2)
            
            hue = i / 360
            rgb = hsl_to_rgb(hue, 0.8, 0.55)
            color = rgb_to_hex(*rgb)
            
            self.wheel_canvas.create_polygon(
                x1, y1, x2, y2, x3, y3, x4, y4,
                fill=color, outline=color, tags="wheel"
            )
        
        # 外圈边框
        self.wheel_canvas.create_oval(cx - r, cy - r, cx + r, cy + r,
                                       outline="#ccc", width=2, tags="wheel")
        self.wheel_canvas.create_oval(cx - self.inner_radius, cy - self.inner_radius,
                                       cx + self.inner_radius, cy + self.inner_radius,
                                       outline="#ccc", width=1, tags="wheel")
    
    def _update_wheel_selection(self):
        """更新色轮上的选择标记"""
        self.wheel_canvas.delete("selection")
        cx, cy = self.wheel_center_x, self.wheel_center_y
        
        for hue in self.selected_hues:
            rad = math.radians(hue - 90)
            mx = cx + (self.wheel_radius + self.inner_radius) / 2 * math.cos(rad)
            my = cy + (self.wheel_radius + self.inner_radius) / 2 * math.sin(rad)
            
            # 白色圆点标记
            self.wheel_canvas.create_oval(
                mx - 8, my - 8, mx + 8, my + 8,
                fill="white", outline="#333", width=2, tags="selection"
            )
            self.wheel_canvas.create_text(
                mx, my, text=str(self.selected_hues.index(hue) + 1),
                font=("Arial", 9, "bold"), tags="selection"
            )
        
        # 更新色块
        for i, c in enumerate(self.swatch_canvases):
            if i < len(self.selected_hues):
                rgb = hue_to_rgb(self.selected_hues[i])
                color = rgb_to_hex(*rgb)
                c.config(bg=color)
            else:
                c.config(bg="#ddd")
    
    # ────────────── 事件处理 ──────────────
    
    def _on_wheel_click(self, event):
        """色轮点击事件"""
        if self.game_over or not self.is_running:
            return
        
        # 计算点击角度
        dx = event.x - self.wheel_center_x
        dy = event.y - self.wheel_center_y
        dist = math.sqrt(dx * dx + dy * dy)
        
        if dist < self.inner_radius or dist > self.wheel_radius:
            return  # 点击在内外圈之外
        
        # 计算色相
        angle = math.degrees(math.atan2(dy, dx)) + 90
        hue = angle % 360
        
        # 检查是否已选择
        level = LEVELS[self.current_level]
        if len(self.selected_hues) >= level.select_count:
            if hue not in self.selected_hues:
                self.status_var.set(f"已达到最大选择数（{level.select_count}个），请先提交或清空")
                return
        
        if hue in self.selected_hues:
            self.selected_hues.remove(hue)
        else:
            self.selected_hues.append(hue)
        
        self._update_wheel_selection()
        self.status_var.set(f"已选择 {len(self.selected_hues)}/{level.select_count} 个颜色")
    
    def _on_wheel_hover(self, event):
        """色轮悬停事件"""
        dx = event.x - self.wheel_center_x
        dy = event.y - self.wheel_center_y
        dist = math.sqrt(dx * dx + dy * dy)
        
        if dist < self.inner_radius or dist > self.wheel_radius:
            self.status_var.set(f"已选择 {len(self.selected_hues)}/{LEVELS[self.current_level].select_count} 个颜色")
            return
        
        angle = math.degrees(math.atan2(dy, dx)) + 90
        hue = angle % 360
        rgb = hue_to_rgb(hue)
        hex_val = rgb_to_hex(*rgb)
        
        self.status_var.set(f"Hover: 色相 {hue:.0f}°  {hex_val}")
    
    def _submit_answer(self):
        """提交答案"""
        if self.game_over or not self.is_running:
            return
        
        level = LEVELS[self.current_level]
        
        if len(self.selected_hues) != level.select_count:
            self.feedback_var.set(f"⚠️ 需要选择 {level.select_count} 个颜色，当前选了 {len(self.selected_hues)} 个")
            return
        
        # 评估
        correct, msg = level.rule_func(self.base_hue, self.selected_hues)
        self.total_attempts += 1
        
        if correct:
            self.correct_attempts += 1
            self.combo += 1
            if self.combo > self.max_combo:
                self.max_combo = self.combo
            
            # 计算得分
            time_bonus = self.time_left * 5
            combo_bonus = self.combo * 10
            score_gain = level.base_score + time_bonus + combo_bonus
            self.score += score_gain
            
            self.feedback_var.set(f"{msg}\n✨ +{score_gain}分（基础{level.base_score}+时间{time_bonus}+连击{combo_bonus}）")
            self._update_score_display()
            
            # 进入下一关
            self.root.after(1500, self._next_level)
        else:
            self.combo = 0
            self.feedback_var.set(msg)
            self._update_score_display()
    
    def _clear_selection(self):
        """清空选择"""
        self.selected_hues.clear()
        self._update_wheel_selection()
        self.status_var.set("选择已清空")
    
    def _show_hint(self):
        """显示提示"""
        level = LEVELS[self.current_level]
        messagebox.showinfo("💡 提示", 
                           f"【{level.name}】\n\n{level.hint}\n\n"
                           f"当前基准色相：{self.base_hue:.0f}°")
    
    def _next_level(self):
        """进入下一关"""
        if self.current_level < len(LEVELS) - 1:
            self.current_level += 1
            self._start_level(self.current_level)
        else:
            self._game_complete()
    
    def _start_level(self, level_id: int):
        """开始指定关卡"""
        level = LEVELS[level_id]
        
        # 停止旧计时器
        if self.timer_id:
            self.root.after_cancel(self.timer_id)
            self.timer_id = None
        
        # 重置状态
        self.selected_hues.clear()
        self.base_hue = random.uniform(0, 359)
        self.time_left = level.time_limit
        self.is_running = True
        self.game_over = False
        
        # 更新界面
        self.level_label.config(text=f"第{level_id+1}关 · {level.name}")
        self.question_var.set(f"{level.desc}\n\n🎯 任务：{level.hint}")
        self.feedback_var.set("等待作答...")
        self.progress_var.set(f"{level_id}/{len(LEVELS)} 关")
        
        # 更新基准色
        base_rgb = hue_to_rgb(self.base_hue)
        base_hex = rgb_to_hex(*base_rgb)
        self.base_canvas.config(bg=base_hex)
        self.base_info.set(f"色相 {self.base_hue:.0f}°  {base_hex}")
        
        # 更新色轮
        self._update_wheel_selection()
        self._draw_base_marker()
        
        # 启动计时器
        self._update_timer()
        
        self.status_var.set(f"第{level_id+1}关开始！选择 {level.select_count} 个颜色后提交")
    
    def _draw_base_marker(self):
        """在色轮上标记基准色位置"""
        self.wheel_canvas.delete("base_marker")
        cx, cy = self.wheel_center_x, self.wheel_center_y
        rad = math.radians(self.base_hue - 90)
        
        x = cx + self.inner_radius * math.cos(rad)
        y = cy + self.inner_radius * math.sin(rad)
        
        self.wheel_canvas.create_oval(
            x - 6, y - 6, x + 6, y + 6,
            fill="#333", outline="white", width=2, tags="base_marker"
        )
    
    def _update_timer(self):
        """更新计时器"""
        if not self.is_running or self.game_over:
            return
        
        mins = self.time_left // 60
        secs = self.time_left % 60
        self.timer_label.config(text=f"⏱ {mins:02d}:{secs:02d}")
        
        if self.time_left <= 5:
            self.timer_label.config(fg="#e74c3c")
        elif self.time_left <= 10:
            self.timer_label.config(fg="#e67e22")
        else:
            self.timer_label.config(fg="#3498db")
        
        if self.time_left <= 0:
            self._time_up()
            return
        
        self.time_left -= 1
        self.timer_id = self.root.after(1000, self._update_timer)
    
    def _time_up(self):
        """时间到"""
        self.is_running = False
        self.combo = 0
        self._update_score_display()
        
        self.feedback_var.set("⏰ 时间到！自动进入下一关")
        self.status_var.set("时间到！")
        
        self.root.after(2000, self._next_level)
    
    def _update_score_display(self):
        """更新分数显示"""
        self.score_label.config(text=f"⭐ {self.score}分")
        self.combo_label.config(text=f"🔥 {self.combo}")
    
    def _game_complete(self):
        """游戏完成"""
        self.game_over = True
        self.is_running = False
        
        if self.timer_id:
            self.root.after_cancel(self.timer_id)
            self.timer_id = None
        
        accuracy = (self.correct_attempts / self.total_attempts * 100) if self.total_attempts > 0 else 0
        
        # 保存排行榜
        entry = {
            "name": "玩家",
            "score": self.score,
            "accuracy": round(accuracy, 1),
            "max_combo": self.max_combo,
            "date": datetime.now().strftime("%Y-%m-%d %H:%M"),
        }
        save_ranking(entry)
        
        # 显示完成对话框
        msg = (
            f"🎉 恭喜通关！\n\n"
            f"最终得分：{self.score} 分\n"
            f"正确率：{accuracy:.1f}%\n"
            f"最高连击：{self.max_combo}\n"
            f"总尝试：{self.total_attempts} 次\n\n"
            f"成绩已保存到排行榜！"
        )
        
        messagebox.showinfo("🎉 游戏完成", msg)
        
        self.question_var.set("🎉 恭喜完成所有关卡！点击下方按钮重新开始")
        self.feedback_var.set(msg)
        
        # 重新开始按钮
        restart_btn = tk.Button(self.root, text="🔄 重新开始", 
                                 command=self._restart_game,
                                 bg="#2ecc71", fg="white",
                                 font=("Microsoft YaHei", 12, "bold"),
                                 padx=20, pady=8)
        restart_btn.pack(pady=10)
    
    def _restart_game(self):
        """重新开始游戏"""
        self.current_level = 0
        self.score = 0
        self.combo = 0
        self.max_combo = 0
        self.total_attempts = 0
        self.correct_attempts = 0
        self.game_over = False
        
        # 移除重新开始按钮
        for w in self.root.winfo_children():
            if isinstance(w, tk.Button) and w.cget("text") == "🔄 重新开始":
                w.destroy()
        
        self._start_level(0)
    
    def _show_ranking(self):
        """显示排行榜"""
        rankings = load_rankings()
        
        if not rankings:
            messagebox.showinfo("📊 排行榜", "暂无记录，快来挑战吧！")
            return
        
        win = tk.Toplevel(self.root)
        win.title("📊 排行榜")
        win.geometry("450x350")
        win.resizable(False, False)
        
        tk.Label(win, text="🏆 排行榜 Top 10", 
                 font=("Microsoft YaHei", 14, "bold"),
                 fg="#2c3e50").pack(pady=10)
        
        # 表格
        frame = tk.Frame(win)
        frame.pack(fill="both", expand=True, padx=20, pady=5)
        
        # 表头
        headers = ["排名", "玩家", "得分", "正确率", "连击"]
        for j, h in enumerate(headers):
            tk.Label(frame, text=h, font=("Microsoft YaHei", 10, "bold"),
                     bg="#3498db", fg="white", padx=5, pady=3,
                     width=8 if j > 0 else 5).grid(row=0, column=j, sticky="ew")
        
        # 数据
        for i, entry in enumerate(rankings):
            row = i + 1
            bg_color = "#f0f8ff" if i % 2 == 0 else "white"
            
            tk.Label(frame, text=f"#{i+1}", font=("Consolas", 10),
                     bg=bg_color, padx=5, pady=2).grid(row=row, column=0)
            tk.Label(frame, text=entry.get("name", "玩家"), 
                     font=("Microsoft YaHei", 10),
                     bg=bg_color, padx=5, pady=2).grid(row=row, column=1)
            tk.Label(frame, text=str(entry.get("score", 0)),
                     font=("Consolas", 10, "bold"), fg="#e67e22",
                     bg=bg_color, padx=5, pady=2).grid(row=row, column=2)
            tk.Label(frame, text=f"{entry.get('accuracy', 0):.0f}%",
                     font=("Consolas", 10),
                     bg=bg_color, padx=5, pady=2).grid(row=row, column=3)
            tk.Label(frame, text=str(entry.get("max_combo", 0)),
                     font=("Consolas", 10), fg="#e74c3c",
                     bg=bg_color, padx=5, pady=2).grid(row=row, column=4)
        
        tk.Button(win, text="关闭", command=win.destroy,
                  bg="#95a5a6", fg="white", padx=20).pack(pady=10)


# ══════════════════════════════════════════════
# 第六部分：启动入口
# ══════════════════════════════════════════════

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