import tkinter as tk
from tkinter import messagebox
import random
import time
import math

# ========================
# 全局配置
# ========================
WIDTH, HEIGHT = 800, 560
PLAY_X, PLAY_Y = 20, 80
PLAY_W, PLAY_H = 760, 420

# ========================
# 主题
# ========================
THEMES = {
    "🌸 樱花粉": {"bg": "#FFF0F5", "fg": "#C2185B", "panel": "#FCE4EC", "btn": "#F48FB1", "accent": "#E91E63", "play": "#FFF5F8", "dot": "#E91E63", "trail": "#F8BBD0", "grid": "#FCE4EC"},
    "💙 天空蓝": {"bg": "#E3F2FD", "fg": "#0D47A1", "panel": "#BBDEFB", "btn": "#42A5F5", "accent": "#1565C0", "play": "#F0F8FF", "dot": "#1565C0", "trail": "#90CAF9", "grid": "#E3F2FD"},
    "💚 抹茶绿": {"bg": "#E8F5E9", "fg": "#1B5E20", "panel": "#C8E6C9", "btn": "#66BB6A", "accent": "#2E7D32", "play": "#F5FFF5", "dot": "#2E7D32", "trail": "#A5D6A7", "grid": "#E8F5E9"},
    "🧡 蜜桔橙": {"bg": "#FFF3E0", "fg": "#E65100", "panel": "#FFE0B2", "btn": "#FF9800", "accent": "#EF6C00", "play": "#FFF8F0", "dot": "#E65100", "trail": "#FFCC80", "grid": "#FFF3E0"},
    "💜 梦幻紫": {"bg": "#F3E5F5", "fg": "#4A148C", "panel": "#E1BEE7", "btn": "#AB47BC", "accent": "#7B1FA2", "play": "#FBF0FF", "dot": "#7B1FA2", "trail": "#CE93D8", "grid": "#F3E5F5"},
    "🖤 竞技黑": {"bg": "#1a1a2e", "fg": "#E0E0E0", "panel": "#16213E", "btn": "#533483", "accent": "#E94560", "play": "#0F3460", "dot": "#E94560", "trail": "#533483", "grid": "#16213E"},
}
cur_theme = "🌸 樱花粉"

# ========================
# 难度配置
# ========================
DIFF_CFG = {
    "简单": {"radius": 30, "lifetime": 3.0, "interval": 1.2, "max_dots": 3, "duration": 30},
    "中等": {"radius": 22, "lifetime": 2.0, "interval": 0.8, "max_dots": 5, "duration": 30},
    "困难": {"radius": 16, "lifetime": 1.4, "interval": 0.5, "max_dots": 7, "duration": 30},
    "地狱": {"radius": 12, "lifetime": 1.0, "interval": 0.35, "max_dots": 10, "duration": 20},
}

# ========================
# 游戏状态（全部提前定义）
# ========================
game_state = "idle"
dots = []
hits = 0
misses = 0
start_time = 0
elapsed = 0
last_spawn = 0
difficulty = "中等"
trails = []
show_grid = True
best_scores = {}

# ========================
# UI 引用
# ========================
root = None
canvas = None
lbl_status = None
lbl_score = None
lbl_time = None
lbl_accuracy = None
lbl_avg = None
lbl_best = None
lbl_diff = None
lbl_log = None
theme_btns = []
diff_btns = {}
all_widgets = []
log_lines = []

# ========================
# 工具函数
# ========================
def log_msg(msg):
    global log_lines
    ts = time.strftime("%H:%M:%S")
    log_lines.append(f"[{ts}] {msg}")
    if len(log_lines) > 30:
        log_lines.pop(0)
    if lbl_log:
        lbl_log.config(state="normal")
        lbl_log.delete("1.0", "end")
        for line in log_lines[-4:]:
            lbl_log.insert("end", line + "\n")
        lbl_log.see("end")
        lbl_log.config(state="disabled")

# ========================
# 圆点管理
# ========================
def spawn_dot():
    global dots
    cfg = DIFF_CFG[difficulty]
    margin = cfg["radius"] + 5
    x = random.randint(PLAY_X + margin, PLAY_X + PLAY_W - margin)
    y = random.randint(PLAY_Y + margin, PLAY_Y + PLAY_H - margin)
    dot = {
        "x": x, "y": y,
        "r": cfg["radius"],
        "born": time.time(),
        "lifetime": cfg["lifetime"],
    }
    dots.append(dot)

def remove_dot(idx, hit=False):
    global hits, misses, trails
    if 0 <= idx < len(dots):
        d = dots.pop(idx)
        if hit:
            hits += 1
            trails.append({"x": d["x"], "y": d["y"], "born": time.time(), "color": "hit"})
            log_msg(f"🎯 命中! (+1) 坐标({d['x']},{d['y']})")
        else:
            misses += 1
            trails.append({"x": d["x"], "y": d["y"], "born": time.time(), "color": "miss"})
            log_msg(f"💨 漏掉! 坐标({d['x']},{d['y']})")

# ========================
# 游戏逻辑
# ========================
def start_game():
    global game_state, dots, hits, misses, start_time
    global elapsed, last_spawn, trails

    dots = []
    trails = []
    hits = 0
    misses = 0
    start_time = time.time()
    elapsed = 0
    last_spawn = 0
    game_state = "playing"

    cfg = DIFF_CFG[difficulty]
    log_msg(f"🚀 开始训练 | 难度:{difficulty} | 时长:{cfg['duration']}s")
    lbl_status.config(text="🎯 开始！点击出现的圆点！", fg=THEMES[cur_theme]["accent"])
    update_display()
    root.after(100, game_loop)

def end_game():
    global game_state
    game_state = "finished"

    total = hits + misses
    acc = (hits / total * 100) if total > 0 else 0
    avg_time = elapsed / hits if hits > 0 else 0

    t = THEMES[cur_theme]

    if acc >= 90 and avg_time < 0.5:
        rank = "🏆 神射手"
    elif acc >= 80:
        rank = "🥇 大师"
    elif acc >= 65:
        rank = "🥈 高手"
    elif acc >= 50:
        rank = "🥉 入门"
    else:
        rank = "💪 加油"

    lbl_status.config(text=f"⏰ 时间到！{rank}", fg=t["accent"])

    # 最佳记录
    best_key = f"best_{difficulty}"
    prev_best = best_scores.get(best_key, {"hits": 0, "acc": 0})
    is_new = False
    if hits > prev_best["hits"]:
        best_scores[best_key] = {"hits": hits, "acc": acc}
        is_new = True

    log_msg(f"🏁 结束 | 命中:{hits} 漏:{misses} 准确率:{acc:.0f}% 平均:{avg_time:.2f}s")
    if is_new:
        log_msg(f"🎉 新纪录！{difficulty} 难度 {hits}命中 {acc:.0f}%")

    update_display()

    msg = f"{rank}\n\n命中: {hits}\n漏掉: {misses}\n准确率: {acc:.1f}%\n平均反应: {avg_time:.2f}s"
    if is_new:
        msg += f"\n\n🎉 新纪录！"
    root.after(500, lambda: messagebox.showinfo("训练结束", msg))

def game_loop():
    global elapsed, last_spawn, dots, trails

    if game_state != "playing":
        return

    now = time.time()
    elapsed = now - start_time
    cfg = DIFF_CFG[difficulty]

    if elapsed >= cfg["duration"]:
        end_game()
        return

    # 生成圆点
    if now - last_spawn >= cfg["interval"] and len(dots) < cfg["max_dots"]:
        spawn_dot()
        last_spawn = now

    # 清理过期圆点
    expired = []
    for i, d in enumerate(dots):
        if now - d["born"] > d["lifetime"]:
            expired.append(i)
    for i in reversed(expired):
        remove_dot(i, hit=False)

    # 清理过期轨迹
    trails = [tr for tr in trails if now - tr["born"] < 0.4]

    update_display()
    draw_canvas()

    root.after(16, game_loop)  # ~60fps

# ========================
# 绘制
# ========================
def draw_canvas():
    canvas.delete("all")
    t = THEMES[cur_theme]

    # 游戏区域背景
    canvas.create_rectangle(PLAY_X, PLAY_Y, PLAY_X + PLAY_W, PLAY_Y + PLAY_H,
                            fill=t["play"], outline=t["accent"], width=2)

    # 网格
    if show_grid:
        for gx in range(PLAY_X, PLAY_X + PLAY_W, 50):
            canvas.create_line(gx, PLAY_Y, gx, PLAY_Y + PLAY_H, fill=t["grid"], width=1)
        for gy in range(PLAY_Y, PLAY_Y + PLAY_H, 50):
            canvas.create_line(PLAY_X, gy, PLAY_X + PLAY_W, gy, fill=t["grid"], width=1)

    now = time.time()

    # 轨迹
    for tr in trails:
        age = now - tr["born"]
        alpha_factor = max(0, 1 - age / 0.4)
        r = int(20 * (1 + age * 2))
        color = t["accent"] if tr["color"] == "hit" else "#999999"
        canvas.create_oval(tr["x"] - r, tr["y"] - r, tr["x"] + r, tr["y"] + r,
                            outline=color, width=1, dash=(3, 3))

    # 圆点
    for d in dots:
        age = now - d["born"]
        life_ratio = age / d["lifetime"] if d["lifetime"] > 0 else 0

        pulse = math.sin(age * 8) * 2
        r = d["r"] + pulse

        if life_ratio < 0.3:
            color = t["dot"]
            outline = t["dot"]
        elif life_ratio < 0.7:
            color = t["dot"]
            outline = "#FF9800"
        else:
            color = "#FF5722"
            outline = "#D32F2F"

        # 光晕
        canvas.create_oval(d["x"] - r - 4, d["y"] - r - 4, d["x"] + r + 4, d["y"] + r + 4,
                            fill="", outline=t["trail"], width=2)
        # 主圆
        canvas.create_oval(d["x"] - r, d["y"] - r, d["x"] + r, d["y"] + r,
                            fill=color, outline=outline, width=2)
        # 中心亮点
        canvas.create_oval(d["x"] - 3, d["y"] - 3, d["x"] + 3, d["y"] + 3,
                            fill="white", outline="")

        # 倒计时弧
        if life_ratio > 0.5:
            arc_extent = 360 * (1 - life_ratio)
            canvas.create_arc(d["x"] - r - 2, d["y"] - r - 2, d["x"] + r + 2, d["y"] + r + 2,
                               start=90, extent=-arc_extent, outline="#FF9800", width=2, style="arc")

    # 时间进度条
    cfg = DIFF_CFG[difficulty]
    progress = min(1, elapsed / cfg["duration"])
    bar_w = PLAY_W - 40
    bx = PLAY_X + 20
    by = PLAY_Y + PLAY_H + 8
    canvas.create_rectangle(bx, by, bx + bar_w, by + 8, fill=t["panel"], outline=t["accent"])
    if progress < 0.5:
        bar_color = "#4CAF50"
    elif progress < 0.8:
        bar_color = "#FF9800"
    else:
        bar_color = "#F44336"
    canvas.create_rectangle(bx, by, bx + bar_w * progress, by + 8, fill=bar_color, outline="")

    # 倒计时文字
    remaining = max(0, cfg["duration"] - elapsed)
    canvas.create_text(PLAY_X + PLAY_W - 10, by + 4, text=f"{remaining:.0f}s",
                        font=("Consolas", 9, "bold"), fill=t["fg"], anchor="e")

# ========================
# 点击检测
# ========================
def on_click(e):
    global misses

    if game_state != "playing":
        return

    if e.x < PLAY_X or e.x > PLAY_X + PLAY_W or e.y < PLAY_Y or e.y > PLAY_Y + PLAY_H:
        return

    hit_idx = -1
    for i in reversed(range(len(dots))):
        d = dots[i]
        dist = math.sqrt((e.x - d["x"])**2 + (e.y - d["y"])**2)
        if dist <= d["r"]:
            hit_idx = i
            break

    if hit_idx >= 0:
        remove_dot(hit_idx, hit=True)
        update_display()
        draw_canvas()
    else:
        misses += 1
        trails.append({"x": e.x, "y": e.y, "born": time.time(), "color": "miss"})
        log_msg(f"💨 空点! 坐标({e.x},{e.y})")
        update_display()
        draw_canvas()

# ========================
# 显示更新
# ========================
def update_display():
    global hits, misses, elapsed
    t = THEMES[cur_theme]

    total = hits + misses
    acc = (hits / total * 100) if total > 0 else 0
    avg = elapsed / hits if hits > 0 else 0

    if lbl_score:
        lbl_score.config(text=f"🎯 {hits}")
    if lbl_accuracy:
        lbl_accuracy.config(text=f"🎯 {acc:.0f}%")
    if lbl_time:
        lbl_time.config(text=f"⏱️ {elapsed:.1f}s")
    if lbl_avg:
        lbl_avg.config(text=f"⚡ {avg:.2f}s/个")

    best_key = f"best_{difficulty}"
    if best_key in best_scores:
        b = best_scores[best_key]
        if lbl_best:
            lbl_best.config(text=f"🏆 最佳:{b['hits']} ({b['acc']:.0f}%)")

# ========================
# 难度设置
# ========================
def set_difficulty(d):
    global difficulty
    difficulty = d
    for name, btn in diff_btns.items():
        if name == d:
            btn.config(relief="sunken", bd=3)
        else:
            btn.config(relief="raised", bd=1)
    if lbl_diff:
        lbl_diff.config(text=f"难度: {d}")
    log_msg(f"⚙️ 难度→{d}")
    if game_state == "idle":
        update_display()

# ========================
# 网格切换
# ========================
def toggle_grid():
    global show_grid
    show_grid = not show_grid
    log_msg(f"📐 网格: {'开' if show_grid else '关'}")
    if game_state != "playing":
        draw_canvas()

# ========================
# 换肤
# ========================
def apply_theme(name):
    global cur_theme
    cur_theme = name
    t = THEMES[name]

    root.config(bg=t["bg"])
    for w in all_widgets:
        try:
            w.config(bg=t["bg"], fg=t["fg"])
        except:
            pass

    for btn in theme_btns:
        btn.config(bg=t["btn"], fg="white")

    if canvas:
        canvas.config(background=t["bg"])

    update_display()
    if game_state != "playing":
        draw_canvas()

# ========================
# 构建界面
# ========================
def build_ui():
    global root, canvas, lbl_status, lbl_score, lbl_time
    global lbl_accuracy, lbl_avg, lbl_best, lbl_diff, lbl_log

    root = tk.Tk()
    root.title("🎯 目标练习 · 鼠标点击训练")
    root.geometry(f"{WIDTH}x{HEIGHT}")
    root.resizable(False, False)

    # ====== 顶部状态栏 ======
    top = tk.Frame(root)
    top.pack(fill="x", pady=2)

    lbl_status = tk.Label(top, text="🎯 准备开始训练！选择难度后点击「开始」", font=("Comic Sans MS", 12, "bold"))
    lbl_status.pack(side="left", padx=10)

    lbl_diff = tk.Label(top, text="难度: 中等", font=("Comic Sans MS", 9))
    lbl_diff.pack(side="right", padx=8)

    # ====== 主题栏 ======
    theme_bar = tk.Frame(root)
    theme_bar.pack(fill="x", pady=1)
    tk.Label(theme_bar, text="🎨 ", font=("", 8)).pack(side="left", padx=3)
    for name in THEMES:
        btn = tk.Button(theme_bar, text=name, font=("Comic Sans MS", 7, "bold"),
                         relief="raised", bd=1, padx=3,
                         command=lambda n=name: apply_theme(n))
        btn.pack(side="left", padx=1)
        theme_btns.append(btn)

    # ====== 设置栏 ======
    setting_bar = tk.Frame(root)
    setting_bar.pack(fill="x", pady=2)

    tk.Label(setting_bar, text="难度:", font=("Comic Sans MS", 9, "bold")).pack(side="left", padx=(8, 3))
    for d in ["简单", "中等", "困难", "地狱"]:
        btn = tk.Button(setting_bar, text=d, font=("Comic Sans MS", 8),
                         relief="raised", bd=1,
                         command=lambda d=d: set_difficulty(d))
        btn.pack(side="left", padx=1)
        diff_btns[d] = btn

    tk.Button(setting_bar, text="📐 网格", font=("Comic Sans MS", 8),
              command=toggle_grid).pack(side="left", padx=5)

    # ====== 画布 ======
    canvas_frame = tk.Frame(root)
    canvas_frame.pack(pady=3)

    canvas = tk.Canvas(canvas_frame, width=WIDTH - 20, height=PLAY_H + 20,
                        highlightthickness=0)
    canvas.pack()
    canvas.bind("<Button-1>", on_click)

    # ====== 统计栏 ======
    stats = tk.Frame(root)
    stats.pack(fill="x", pady=2)

    lbl_score = tk.Label(stats, text="🎯 0", font=("Comic Sans MS", 12, "bold"))
    lbl_score.pack(side="left", padx=15)

    lbl_time = tk.Label(stats, text="⏱️ 0.0s", font=("Comic Sans MS", 11))
    lbl_time.pack(side="left", padx=10)

    lbl_accuracy = tk.Label(stats, text="🎯 0%", font=("Comic Sans MS", 11))
    lbl_accuracy.pack(side="left", padx=10)

    lbl_avg = tk.Label(stats, text="⚡ 0.00s/个", font=("Comic Sans MS", 11))
    lbl_avg.pack(side="left", padx=10)

    lbl_best = tk.Label(stats, text="🏆 最佳:--", font=("Comic Sans MS", 10))
    lbl_best.pack(side="left", padx=10)

    # ====== 按钮栏 ======
    btn_bar = tk.Frame(root)
    btn_bar.pack(fill="x", pady=3)

    tk.Button(btn_bar, text="▶️ 开始训练", font=("Comic Sans MS", 12, "bold"),
              bg="#4CAF50", fg="white", padx=15, command=start_game).pack(side="left", padx=10)
    tk.Button(btn_bar, text="⏹️ 停止", font=("Comic Sans MS", 10),
              bg="#F44336", fg="white", padx=10,
              command=lambda: stop_game()).pack(side="left", padx=3)

    # ====== 底部日志 ======
    log_frame = tk.Frame(root)
    log_frame.pack(fill="x", side="bottom", pady=1)

    scroll = tk.Scrollbar(log_frame)
    scroll.pack(side="right", fill="y")

    lbl_log = tk.Text(log_frame, font=("Consolas", 7), height=3, yscrollcommand=scroll.set)
    lbl_log.pack(fill="both", expand=True, padx=3)
    scroll.config(command=lbl_log.yview)
    lbl_log.config(state="disabled")

    # ====== 收集控件 ======
    all_widgets.extend([top, theme_bar, setting_bar, canvas_frame, stats, btn_bar, log_frame])
    all_widgets.extend([lbl_status, lbl_diff, lbl_score, lbl_time, lbl_accuracy, lbl_avg, lbl_best])

# ========================
# 停止游戏
# ========================
def stop_game():
    global game_state
    if game_state == "playing":
        game_state = "idle"
        lbl_status.config(text="⏹️ 已停止 | 点击「开始」重新训练", fg=THEMES[cur_theme]["accent"])
        log_msg("⏹️ 训练已停止")
        update_display()

# ========================
# 初始化
# ========================
def init():
    build_ui()
    apply_theme("🌸 樱花粉")
    set_difficulty("中等")
    draw_canvas()
    log_msg("🎯 目标练习已就绪！")
    log_msg("💡 圆点出现后快速点击，越小越快消失的越难！")
    log_msg("⚡ 提示：不要猛甩鼠标，平滑移动更准")
    update_display()

# ========================
# 启动
# ========================
if __name__ == "__main__":
    root = None
    canvas = None
    lbl_status = None
    lbl_score = None
    lbl_time = None
    lbl_accuracy = None
    lbl_avg = None
    lbl_best = None
    lbl_diff = None
    lbl_log = None
    init()
    root.mainloop()
