"""单步调试面板。"""

from __future__ import annotations

import csv
import datetime
import os
import tempfile
import threading
import tkinter as tk
from tkinter import filedialog, messagebox

import ttkbootstrap as ttk
from PIL import Image

from app.views.modern_styles import apply_listbox_theme

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pqAutomationApp import PQAutomationApp






_DEFAULT_SAMPLES = [
    {"name": "Red Sample", "hex": "#D22630", "x": "0.6400", "y": "0.3300"},
    {"name": "Green Sample", "hex": "#00A651", "x": "0.3000", "y": "0.6000"},
    {"name": "Blue Sample", "hex": "#21409A", "x": "0.1500", "y": "0.0600"},
    {"name": "Orange Sample", "hex": "#FF6A13", "x": "0.5500", "y": "0.4000"},
    {"name": "Violet Sample", "hex": "#6A0DAD", "x": "0.2700", "y": "0.1400"},
    {"name": "Gray Sample", "hex": "#8A8D8F", "x": "0.3127", "y": "0.3290"},
]


def create_single_step_panel(self: "PQAutomationApp"):
    """创建单步调试面板。"""
    frame = ttk.Frame(self.content_frame)
    self.single_step_frame = frame
    self.single_step_visible = False
    self.single_step_samples = []
    self.single_step_results = []
    self.single_step_current_index = None
    self.single_step_current_image_path = None

    root = ttk.Frame(frame, padding=10)
    root.pack(fill=tk.BOTH, expand=True)
    root.columnconfigure(0, weight=0)
    root.columnconfigure(1, weight=1)
    root.rowconfigure(1, weight=1)

    title_row = ttk.Frame(root)
    title_row.grid(row=0, column=0, columnspan=2, sticky=tk.EW, pady=(0, 10))
    ttk.Label(
        title_row,
        text="单步调试",
        font=("微软雅黑", 14, "bold"),
    ).pack(side=tk.LEFT)
    ttk.Label(
        title_row,
        text="录入目标色块，发送纯色色块并采集 xyY / ΔE2000。",
        style="Muted.TLabel",
    ).pack(side=tk.LEFT, padx=(12, 0))

    left = ttk.LabelFrame(root, text="样本列表", padding=8)
    left.grid(row=1, column=0, sticky=tk.NS, padx=(0, 10))
    left.grid_propagate(False)
    left.configure(width=340)

    self.single_step_listbox = tk.Listbox(
        left,
        width=34,
        activestyle="none",
        font=("微软雅黑", 9),
        highlightthickness=1,
    )
    apply_listbox_theme(self.single_step_listbox)
    self.single_step_listbox.pack(fill=tk.BOTH, expand=True)
    self.single_step_listbox.bind(
        "<<ListboxSelect>>", lambda e: _on_sample_select(self)
    )

    list_btn_row = ttk.Frame(left)
    list_btn_row.pack(fill=tk.X, pady=(8, 0))
    ttk.Button(
        list_btn_row,
        text="导入 CSV",
        bootstyle="secondary-outline",
        command=lambda: _import_samples_csv(self),
    ).pack(side=tk.LEFT, padx=(0, 4))
    ttk.Button(
        list_btn_row,
        text="载入示例",
        bootstyle="secondary-outline",
        command=lambda: _load_default_samples(self),
    ).pack(side=tk.LEFT, padx=(0, 4))
    ttk.Button(
        list_btn_row,
        text="删除",
        bootstyle="danger-outline",
        command=lambda: _delete_current_sample(self),
    ).pack(side=tk.LEFT)

    right = ttk.Frame(root)
    right.grid(row=1, column=1, sticky=tk.NSEW)

    form_frame = ttk.LabelFrame(right, text="样本配置", padding=8)
    form_frame.pack(fill=tk.X)
    for column in range(6):
        form_frame.columnconfigure(column, weight=1 if column in (1, 3, 5) else 0)

    self.single_step_name_var = tk.StringVar()
    self.single_step_hex_var = tk.StringVar(value="#FFFFFF")
    self.single_step_target_x_var = tk.StringVar()
    self.single_step_target_y_var = tk.StringVar()
    self.single_step_measured_x_var = tk.StringVar()
    self.single_step_measured_y_var = tk.StringVar()
    self.single_step_measured_lv_var = tk.StringVar()
    self.single_step_status_var = tk.StringVar(value="未选择样本")

    ttk.Label(form_frame, text="名称").grid(row=0, column=0, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_name_var).grid(
        row=0, column=1, sticky=tk.EW, padx=(0, 8), pady=4
    )
    ttk.Label(form_frame, text="HEX").grid(row=0, column=2, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_hex_var, width=12).grid(
        row=0, column=3, sticky=tk.EW, padx=(0, 8), pady=4
    )
    ttk.Label(form_frame, text="目标 x").grid(row=0, column=4, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_target_x_var, width=10).grid(
        row=0, column=5, sticky=tk.EW, pady=4
    )

    ttk.Label(form_frame, text="目标 y").grid(row=1, column=0, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_target_y_var, width=10).grid(
        row=1, column=1, sticky=tk.EW, padx=(0, 8), pady=4
    )
    ttk.Label(form_frame, text="实测 x").grid(row=1, column=2, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_measured_x_var, width=10).grid(
        row=1, column=3, sticky=tk.EW, padx=(0, 8), pady=4
    )
    ttk.Label(form_frame, text="实测 y").grid(row=1, column=4, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_measured_y_var, width=10).grid(
        row=1, column=5, sticky=tk.EW, pady=4
    )

    ttk.Label(form_frame, text="实测 Lv").grid(row=2, column=0, sticky=tk.W, pady=4)
    ttk.Entry(form_frame, textvariable=self.single_step_measured_lv_var, width=10).grid(
        row=2, column=1, sticky=tk.EW, padx=(0, 8), pady=4
    )
    ttk.Label(
        form_frame,
        textvariable=self.single_step_status_var,
        style="Muted.TLabel",
    ).grid(row=2, column=2, columnspan=4, sticky=tk.W, pady=4)

    action_row = ttk.Frame(form_frame)
    action_row.grid(row=3, column=0, columnspan=6, sticky=tk.EW, pady=(6, 0))
    ttk.Button(
        action_row,
        text="新增 / 更新样本",
        bootstyle="primary",
        command=lambda: _upsert_sample(self),
    ).pack(side=tk.LEFT, padx=(0, 6))
    ttk.Button(
        action_row,
        text="发送当前色块",
        bootstyle="info-outline",
        command=lambda: _send_current_patch(self),
    ).pack(side=tk.LEFT, padx=(0, 6))
    ttk.Button(
        action_row,
        text="CA410 采集",
        bootstyle="success-outline",
        command=lambda: _measure_current_sample(self),
    ).pack(side=tk.LEFT, padx=(0, 6))
    ttk.Button(
        action_row,
        text="记录结果",
        bootstyle="warning-outline",
        command=lambda: _commit_result(self),
    ).pack(side=tk.LEFT)

    result_frame = ttk.LabelFrame(right, text="调试结果", padding=8)
    result_frame.pack(fill=tk.BOTH, expand=True, pady=(10, 0))

    columns = (
        "name",
        "hex",
        "target_x",
        "target_y",
        "measured_x",
        "measured_y",
        "lv",
        "delta_e",
        "time",
    )
    self.single_step_result_tree = ttk.Treeview(
        result_frame,
        columns=columns,
        show="headings",
        height=12,
    )
    headings = {
        "name": "名称",
        "hex": "HEX",
        "target_x": "目标 x",
        "target_y": "目标 y",
        "measured_x": "实测 x",
        "measured_y": "实测 y",
        "lv": "Lv",
        "delta_e": "ΔE2000",
        "time": "时间",
    }
    widths = {
        "name": 130,
        "hex": 90,
        "target_x": 80,
        "target_y": 80,
        "measured_x": 80,
        "measured_y": 80,
        "lv": 80,
        "delta_e": 90,
        "time": 120,
    }
    for column in columns:
        self.single_step_result_tree.heading(column, text=headings[column])
        self.single_step_result_tree.column(
            column, width=widths[column], anchor=tk.CENTER
        )
    self.single_step_result_tree.pack(fill=tk.BOTH, expand=True)

    bottom_row = ttk.Frame(right)
    bottom_row.pack(fill=tk.X, pady=(8, 0))
    ttk.Button(
        bottom_row,
        text="导出结果 CSV",
        bootstyle="success",
        command=lambda: _export_results_csv(self),
    ).pack(side=tk.LEFT, padx=(0, 6))
    ttk.Button(
        bottom_row,
        text="清空结果",
        bootstyle="danger-outline",
        command=lambda: _clear_results(self),
    ).pack(side=tk.LEFT)

    self.register_panel("single_step", frame, None, "single_step_visible")
    _load_default_samples(self)


def toggle_single_step_panel(self: "PQAutomationApp"):
    """切换单步调试面板。"""
    self.show_panel("single_step")


def _load_default_samples(self: "PQAutomationApp"):
    self.single_step_samples = [dict(item) for item in _DEFAULT_SAMPLES]
    _refresh_sample_list(self, select_index=0 if self.single_step_samples else None)
    self.single_step_status_var.set(
        f"已载入 {len(self.single_step_samples)} 个示例样本"
    )


def _refresh_sample_list(self: "PQAutomationApp", select_index=None):
    self.single_step_listbox.delete(0, tk.END)
    for sample in self.single_step_samples:
        self.single_step_listbox.insert(
            tk.END,
            f"{sample['name']}  {sample['hex']}  ({sample['x']}, {sample['y']})",
        )
    if select_index is not None and 0 <= select_index < len(self.single_step_samples):
        self.single_step_listbox.selection_clear(0, tk.END)
        self.single_step_listbox.selection_set(select_index)
        self.single_step_listbox.activate(select_index)
        _select_sample(self, select_index)
    elif not self.single_step_samples:
        self.single_step_current_index = None
        self.single_step_name_var.set("")
        self.single_step_hex_var.set("#FFFFFF")
        self.single_step_target_x_var.set("")
        self.single_step_target_y_var.set("")
        self.single_step_status_var.set("样本列表为空")


def _on_sample_select(self: "PQAutomationApp"):
    selection = self.single_step_listbox.curselection()
    if not selection:
        return
    _select_sample(self, selection[0])


def _select_sample(self: "PQAutomationApp", index):
    sample = self.single_step_samples[index]
    self.single_step_current_index = index
    self.single_step_name_var.set(sample["name"])
    self.single_step_hex_var.set(sample["hex"])
    self.single_step_target_x_var.set(sample["x"])
    self.single_step_target_y_var.set(sample["y"])
    self.single_step_status_var.set(f"当前样本: {sample['name']}")


def _import_samples_csv(self: "PQAutomationApp"):
    path = filedialog.askopenfilename(
        title="选择单步调试样本 CSV",
        filetypes=[("CSV 文件", "*.csv"), ("所有文件", "*.*")],
    )
    if not path:
        return
    samples = []
    try:
        with open(path, "r", encoding="utf-8-sig", newline="") as fp:
            reader = csv.DictReader(fp)
            for row in reader:
                name = (row.get("name") or row.get("sample") or "").strip()
                hex_value = (row.get("hex") or row.get("rgb") or "").strip()
                target_x = (row.get("x") or "").strip()
                target_y = (row.get("y") or "").strip()
                if not name or not hex_value or not target_x or not target_y:
                    continue
                samples.append(
                    {
                        "name": name,
                        "hex": _normalize_hex(hex_value),
                        "x": target_x,
                        "y": target_y,
                    }
                )
    except Exception as exc:
        messagebox.showerror("导入失败", f"读取 CSV 失败: {exc}")
        return
    if not samples:
        messagebox.showwarning("导入失败", "CSV 中没有有效样本，要求列包含 name/hex/x/y")
        return
    self.single_step_samples = samples
    _refresh_sample_list(self, select_index=0)
    self.log_gui.log(f"单步调试样本已导入: {len(samples)} 条", level="success")


def _delete_current_sample(self: "PQAutomationApp"):
    if self.single_step_current_index is None:
        return
    removed = self.single_step_samples.pop(self.single_step_current_index)
    next_index = min(self.single_step_current_index, len(self.single_step_samples) - 1)
    _refresh_sample_list(self, select_index=next_index if next_index >= 0 else None)
    self.single_step_status_var.set(f"已删除样本: {removed['name']}")


def _upsert_sample(self: "PQAutomationApp"):
    try:
        sample = {
            "name": self.single_step_name_var.get().strip(),
            "hex": _normalize_hex(self.single_step_hex_var.get()),
            "x": _format_float(self.single_step_target_x_var.get()),
            "y": _format_float(self.single_step_target_y_var.get()),
        }
    except ValueError as exc:
        messagebox.showerror("参数错误", str(exc))
        return
    if not sample["name"]:
        messagebox.showwarning("提示", "请输入样本名称")
        return
    if self.single_step_current_index is None:
        self.single_step_samples.append(sample)
        select_index = len(self.single_step_samples) - 1
        self.single_step_status_var.set(f"已新增样本: {sample['name']}")
    else:
        self.single_step_samples[self.single_step_current_index] = sample
        select_index = self.single_step_current_index
        self.single_step_status_var.set(f"已更新样本: {sample['name']}")
    _refresh_sample_list(self, select_index=select_index)


def _normalize_hex(value):
    text = (value or "").strip().upper()
    if not text:
        raise ValueError("HEX 不能为空")
    if not text.startswith("#"):
        text = "#" + text
    if len(text) != 7 or any(ch not in "#0123456789ABCDEF" for ch in text):
        raise ValueError("HEX 格式应为 #RRGGBB")
    return text


def _format_float(value):
    try:
        number = float(str(value).strip())
    except Exception as exc:
        raise ValueError("xy 值必须是数字") from exc
    return f"{number:.4f}"


def _build_color_patch(self: "PQAutomationApp", hex_value):
    if not self.signal_service.is_connected:
        raise RuntimeError("请先连接 UCD323 设备")
    width, height = self.signal_service.current_resolution()
    rgb = tuple(int(hex_value[i:i + 2], 16) for i in (1, 3, 5))
    temp_dir = os.path.join(tempfile.gettempdir(), "pq_single_step_patches")
    os.makedirs(temp_dir, exist_ok=True)
    file_path = os.path.join(
        temp_dir, f"single_step_{hex_value[1:]}_{width}x{height}.png"
    )
    Image.new("RGB", (width, height), rgb).save(file_path, format="PNG")
    return file_path


def _send_current_patch(self: "PQAutomationApp"):
    if self.single_step_current_index is None:
        messagebox.showinfo("提示", "请先选择一个样本")
        return
    sample = self.single_step_samples[self.single_step_current_index]

    def worker():
        try:
            image_path = _build_color_patch(self, sample["hex"])
            self.signal_service.send_image(image_path)
            self.single_step_current_image_path = image_path
            self._dispatch_ui(
                self.single_step_status_var.set,
                f"已发送色块: {sample['name']} {sample['hex']}",
            )
            self._dispatch_ui(
                self.log_gui.log,
                f"单步调试色块已发送: {sample['name']} {sample['hex']}",
                "success",
            )
        except Exception as exc:
            self._dispatch_ui(self.single_step_status_var.set, f"发送失败: {exc}")
            self._dispatch_ui(self.log_gui.log, f"单步调试色块发送失败: {exc}", "error")

    threading.Thread(target=worker, daemon=True).start()


def _measure_current_sample(self: "PQAutomationApp"):
    if self.single_step_current_index is None:
        messagebox.showinfo("提示", "请先选择一个样本")
        return
    if not getattr(self, "ca", None):
        messagebox.showwarning("警告", "请先连接 CA410 色度计")
        return

    def worker():
        try:
            x, y, lv, _X, _Y, _Z = self.read_ca_xyLv()
            if lv is None:
                raise RuntimeError("CA410 未返回有效亮度")
            self._dispatch_ui(self.single_step_measured_x_var.set, f"{x:.4f}")
            self._dispatch_ui(self.single_step_measured_y_var.set, f"{y:.4f}")
            self._dispatch_ui(self.single_step_measured_lv_var.set, f"{lv:.2f}")
            self._dispatch_ui(self.single_step_status_var.set, "采集完成，可记录结果")
            self._dispatch_ui(
                self.log_gui.log,
                f"单步调试采集完成: x={x:.4f}, y={y:.4f}, Lv={lv:.2f}",
                "success",
            )
        except Exception as exc:
            self._dispatch_ui(self.single_step_status_var.set, f"采集失败: {exc}")
            self._dispatch_ui(self.log_gui.log, f"单步调试采集失败: {exc}", "error")

    threading.Thread(target=worker, daemon=True).start()


def _commit_result(self: "PQAutomationApp"):
    if self.single_step_current_index is None:
        messagebox.showinfo("提示", "请先选择一个样本")
        return
    sample = self.single_step_samples[self.single_step_current_index]
    try:
        measured_x = float(self.single_step_measured_x_var.get().strip())
        measured_y = float(self.single_step_measured_y_var.get().strip())
        measured_lv = float(self.single_step_measured_lv_var.get().strip())
        target_x = float(sample["x"])
        target_y = float(sample["y"])
        delta_e = self.calculate_delta_e_2000(
            measured_x, measured_y, measured_lv, target_x, target_y
        )
    except Exception as exc:
        messagebox.showerror("记录失败", f"请先准备完整的目标值与实测值: {exc}")
        return

    timestamp = datetime.datetime.now().strftime("%H:%M:%S")
    record = {
        "name": sample["name"],
        "hex": sample["hex"],
        "target_x": f"{target_x:.4f}",
        "target_y": f"{target_y:.4f}",
        "measured_x": f"{measured_x:.4f}",
        "measured_y": f"{measured_y:.4f}",
        "lv": f"{measured_lv:.2f}",
        "delta_e": f"{delta_e:.3f}",
        "time": timestamp,
    }
    self.single_step_results.append(record)
    self.single_step_result_tree.insert(
        "",
        tk.END,
        values=tuple(
            record[key]
            for key in (
                "name",
                "hex",
                "target_x",
                "target_y",
                "measured_x",
                "measured_y",
                "lv",
                "delta_e",
                "time",
            )
        ),
    )
    self.single_step_status_var.set(f"已记录结果，ΔE2000={record['delta_e']}")


def _clear_results(self: "PQAutomationApp"):
    self.single_step_results = []
    for item in self.single_step_result_tree.get_children():
        self.single_step_result_tree.delete(item)
    self.single_step_status_var.set("结果已清空")


def _export_results_csv(self: "PQAutomationApp"):
    if not self.single_step_results:
        messagebox.showinfo("提示", "暂无可导出的调试结果")
        return
    path = filedialog.asksaveasfilename(
        title="导出单步调试结果",
        defaultextension=".csv",
        initialfile=f"single_step_debug_{datetime.datetime.now().strftime('%Y%m%d_%H%M%S')}.csv",
        filetypes=[("CSV 文件", "*.csv")],
    )
    if not path:
        return
    fieldnames = [
        "name",
        "hex",
        "target_x",
        "target_y",
        "measured_x",
        "measured_y",
        "lv",
        "delta_e",
        "time",
    ]
    try:
        with open(path, "w", encoding="utf-8-sig", newline="") as fp:
            writer = csv.DictWriter(fp, fieldnames=fieldnames)
            writer.writeheader()
            writer.writerows(self.single_step_results)
        self.log_gui.log(f"单步调试结果已导出: {path}", level="success")
        self.single_step_status_var.set(f"结果已导出: {os.path.basename(path)}")
    except Exception as exc:
        messagebox.showerror("导出失败", f"写入 CSV 失败: {exc}")


def refresh_single_step_theme(self: "PQAutomationApp"):
    """刷新单步调试中 tk.Listbox 的主题色。"""
    if hasattr(self, "single_step_listbox"):
        apply_listbox_theme(self.single_step_listbox)


class SingleStepPanelMixin:
    """由 tools/refactor_to_mixins.py 自动生成。
    把本模块的自由函数挂到 PQAutomationApp 上，便于 F12 跳转与类型推断。
    """
    create_single_step_panel = create_single_step_panel
    toggle_single_step_panel = toggle_single_step_panel
    _load_default_samples = _load_default_samples
    _refresh_sample_list = _refresh_sample_list
    _on_sample_select = _on_sample_select
    _select_sample = _select_sample
    _import_samples_csv = _import_samples_csv
    _delete_current_sample = _delete_current_sample
    _upsert_sample = _upsert_sample
    _build_color_patch = _build_color_patch
    _send_current_patch = _send_current_patch
    _measure_current_sample = _measure_current_sample
    _commit_result = _commit_result
    _clear_results = _clear_results
    _export_results_csv = _export_results_csv
    refresh_single_step_theme = refresh_single_step_theme
