"""CCT / 色度一致性绘制。

Step 2 重构：从 pqAutomationApp.PQAutomationApp.plot_cct 原样搬迁。
"""

import numpy as np
from app.views.modern_styles import get_theme_palette

from typing import TYPE_CHECKING

if TYPE_CHECKING:
    from pqAutomationApp import PQAutomationApp


def _is_dark_palette(palette: dict[str, str]) -> bool:
    """根据主题背景色亮度判断是否深色主题。"""
    bg = palette.get("bg", "#FFFFFF").lstrip("#")
    try:
        r = int(bg[0:2], 16)
        g = int(bg[2:4], 16)
        b = int(bg[4:6], 16)
    except Exception:
        return False
    return (r * 299 + g * 587 + b * 114) / 1000 < 128



def plot_cct(self: "PQAutomationApp", test_type):
    """绘制 x 和 y 坐标分离图 - 每个点标注纵坐标值"""
    palette = get_theme_palette()
    dark_mode = _is_dark_palette(palette)
    x_line_color = "#2F8BFF" if dark_mode else "#0A4BFF"
    y_line_color = "#FF4D4D" if dark_mode else "#D90429"
    ideal_line_color = "#00C853" if dark_mode else "#198754"
    x_tol_color = "#FF7070" if dark_mode else "#C0392B"
    y_tol_color = "#FFB74D" if dark_mode else "#D68910"
    grid_color = "#566070" if dark_mode else "#B8BDC3"
    axis_text = "#EDF2FA" if dark_mode else palette["fg"]
    axis_sub_text = "#C8D2E0" if dark_mode else "#222222"
    legend_bg = "#131821" if dark_mode else "#FFFFFF"
    legend_edge = "#A4B2C6" if dark_mode else palette["border"]

    self.cct_fig.clear()
    self.cct_fig.patch.set_facecolor(palette["bg"])

    gray_data = self.results.get_intermediate_data("shared", "gray")
    if not gray_data:
        gray_data = self.results.get_intermediate_data("cct", "gray")

    if not gray_data or len(gray_data) < 2:
        self.log_gui.log("无 xy 数据可用", level="error")
        ax = self.cct_fig.add_subplot(111)
        ax.text(
            0.5,
            0.5,
            "无可用数据",
            ha="center",
            va="center",
            fontsize=14,
            color=palette["danger"],
        )
        ax.axis("off")
        self.cct_canvas.draw()
        return

    x_measured = [data[0] for data in gray_data]
    y_measured = [data[1] for data in gray_data]

    # 反转数据顺序（从暗到亮）
    x_measured = x_measured[::-1]
    y_measured = y_measured[::-1]

    # 去掉第一个点
    x_measured = x_measured[1:]
    y_measured = y_measured[1:]

    # 重新生成灰阶坐标
    total_points = len(gray_data)
    grayscale = np.linspace(100 / total_points, 100, len(x_measured))

    self.log_gui.log(f"已移除第一个数据点，当前数据点数: {len(x_measured)}", level="success")
    self.log_gui.log(f"  x范围: {min(x_measured):.6f} - {max(x_measured):.6f}", level="info")
    self.log_gui.log(f"  y范围: {min(y_measured):.6f} - {max(y_measured):.6f}", level="info")

    # ========== 根据测试类型读取对应参数 ==========
    # 屏模组中心坐标优先使用实测 100% 灰阶点（gray 数据第 1 个点）
    screen_center_xy = None
    if test_type == "screen_module":
        try:
            if gray_data and len(gray_data[0]) >= 2:
                screen_center_xy = (float(gray_data[0][0]), float(gray_data[0][1]))
        except Exception:
            screen_center_xy = None

    if test_type == "sdr_movie":
        try:
            x_ideal = float(self.sdr_cct_x_ideal_var.get())
            x_tolerance = float(self.sdr_cct_x_tolerance_var.get())
            y_ideal = float(self.sdr_cct_y_ideal_var.get())
            y_tolerance = float(self.sdr_cct_y_tolerance_var.get())
            self.log_gui.log("使用 SDR 色度参数", level="success")
        except:
            x_ideal = 0.3127
            x_tolerance = 0.003
            y_ideal = 0.3290
            y_tolerance = 0.003
            self.log_gui.log("SDR 参数读取失败，使用默认值", level="error")
    elif test_type == "hdr_movie":
        try:
            x_ideal = float(self.hdr_cct_x_ideal_var.get())
            x_tolerance = float(self.hdr_cct_x_tolerance_var.get())
            y_ideal = float(self.hdr_cct_y_ideal_var.get())
            y_tolerance = float(self.hdr_cct_y_tolerance_var.get())
            self.log_gui.log("使用 HDR 色度参数", level="success")
        except:
            x_ideal = 0.3127
            x_tolerance = 0.003
            y_ideal = 0.3290
            y_tolerance = 0.003
            self.log_gui.log("HDR 参数读取失败，使用默认值", level="error")
    else:  # screen_module
        try:
            x_tolerance = float(self.cct_x_tolerance_var.get())
            y_tolerance = float(self.cct_y_tolerance_var.get())
            if screen_center_xy is not None:
                x_ideal, y_ideal = screen_center_xy
                # 同步到输入框，避免界面显示和实际计算不一致
                try:
                    self.cct_x_ideal_var.set(f"{x_ideal:.6f}")
                    self.cct_y_ideal_var.set(f"{y_ideal:.6f}")
                except Exception:
                    pass
                self.log_gui.log(
                    f"屏模组中心使用实测100%坐标: x={x_ideal:.6f}, y={y_ideal:.6f}"
                , level="success")
            else:
                x_ideal = float(self.cct_x_ideal_var.get())
                y_ideal = float(self.cct_y_ideal_var.get())
                self.log_gui.log("未取到实测100%点，回退到屏模组色度参数", level="error")
        except:
            x_ideal = 0.306
            x_tolerance = 0.003
            y_ideal = 0.318
            y_tolerance = 0.003
            self.log_gui.log("屏模组参数读取失败，使用默认值", level="error")

    x_low = x_ideal - x_tolerance
    x_high = x_ideal + x_tolerance
    y_low = y_ideal - y_tolerance
    y_high = y_ideal + y_tolerance

    self.log_gui.log(f"用户设置参数:", level="success")
    self.log_gui.log(f"  x-ideal={x_ideal:.4f}, tolerance={x_tolerance:.4f}", level="info")
    self.log_gui.log(f"  x范围: [{x_low:.4f}, {x_high:.4f}]", level="info")
    self.log_gui.log(f"  y-ideal={y_ideal:.4f}, tolerance={y_tolerance:.4f}", level="info")
    self.log_gui.log(f"  y范围: [{y_low:.4f}, {y_high:.4f}]", level="info")

    # 为所有测试类型创建子图
    ax1 = self.cct_fig.add_subplot(211)
    ax2 = self.cct_fig.add_subplot(212)
    for ax in (ax1, ax2):
        ax.set_facecolor(palette["card_bg"])
        for spine in ax.spines.values():
            spine.set_color(palette["border"])
        ax.tick_params(labelsize=8, colors=axis_sub_text)
        ax.xaxis.label.set_color(axis_text)
        ax.yaxis.label.set_color(axis_text)

    # ========== 上图：x coordinates ==========
    ax1.plot(
        grayscale,
        x_measured,
        color=x_line_color,
        marker="o",
        label="屏本体",
        linewidth=2,
        markersize=4,
        zorder=5,
    )

    # 为每个点添加数值标注（x 坐标）
    for i, (gs, x_val) in enumerate(zip(grayscale, x_measured)):
        ax1.annotate(
            f"{x_val:.5f}",
            xy=(gs, x_val),
            xytext=(0, 8),
            textcoords="offset points",
            ha="center",
            va="bottom",
            fontsize=7,
            color=x_line_color,
            bbox=dict(
                boxstyle="round,pad=0.2",
                facecolor=palette["card_bg"],
                edgecolor=x_line_color,
                alpha=0.92 if dark_mode else 0.85,
                linewidth=0.8,
            ),
        )

    # 绘制完整的参考线
    full_grayscale = np.linspace(0, 100, 100)
    ax1.axhline(
        y=x_ideal,
        color=ideal_line_color,
        linestyle="--",
        linewidth=1.5,
        label=f"x-ideal ({x_ideal:.4f})",
        zorder=3,
    )
    ax1.axhline(
        y=x_low,
        color=x_tol_color,
        linestyle=":",
        linewidth=1,
        alpha=0.95 if dark_mode else 0.7,
        label=f"x-low ({x_low:.4f})",
        zorder=2,
    )
    ax1.axhline(
        y=x_high,
        color=x_tol_color,
        linestyle=":",
        linewidth=1,
        alpha=0.95 if dark_mode else 0.7,
        label=f"x-high ({x_high:.4f})",
        zorder=2,
    )
    ax1.fill_between(
        full_grayscale,
        x_low,
        x_high,
        alpha=0.22 if dark_mode else 0.15,
        color=x_line_color,
        zorder=1,
    )

    ax1.set_xlabel("灰阶 (%)", fontsize=9, color=axis_text)
    ax1.set_ylabel("CIE x", fontsize=9, color=axis_text)
    ax1.grid(True, linestyle="--", alpha=0.45 if dark_mode else 0.3, color=grid_color)
    ax1.set_xlim(0, 105)

    # 纵坐标范围由用户参数控制
    x_min_data = min(x_measured)
    x_max_data = max(x_measured)
    data_range_x = x_max_data - x_min_data

    self.log_gui.log(f"  x数据波动: {data_range_x:.6f}", level="info")

    range_span = x_tolerance * 2
    margin_ratio = 0.20
    extra_margin = range_span * margin_ratio

    final_y_min = min(x_min_data, x_low) - extra_margin
    final_y_max = max(x_max_data, x_high) + extra_margin

    if x_min_data >= x_low and x_max_data <= x_high:
        self.log_gui.log(f"  x数据在tolerance范围内，使用tolerance范围显示", level="info")
        final_y_min = x_low - extra_margin
        final_y_max = x_high + extra_margin
    else:
        self.log_gui.log(f"  x数据超出tolerance范围，扩展显示范围", level="info")

    ax1.set_ylim(final_y_min, final_y_max)
    self.log_gui.log(
        f"  x轴显示范围: {final_y_min:.6f} - {final_y_max:.6f} (跨度: {final_y_max - final_y_min:.6f})"
    , level="info")

    # ========== 下图：y coordinates ==========
    ax2.plot(
        grayscale,
        y_measured,
        color=y_line_color,
        marker="o",
        label="屏本体",
        linewidth=2,
        markersize=4,
        zorder=5,
    )

    # 为每个点添加数值标注（y 坐标）
    for i, (gs, y_val) in enumerate(zip(grayscale, y_measured)):
        ax2.annotate(
            f"{y_val:.5f}",
            xy=(gs, y_val),
            xytext=(0, 8),
            textcoords="offset points",
            ha="center",
            va="bottom",
            fontsize=7,
            color=y_line_color,
            bbox=dict(
                boxstyle="round,pad=0.2",
                facecolor=palette["card_bg"],
                edgecolor=y_line_color,
                alpha=0.92 if dark_mode else 0.85,
                linewidth=0.8,
            ),
        )

    ax2.axhline(
        y=y_ideal,
        color=ideal_line_color,
        linestyle="--",
        linewidth=1.5,
        label=f"y-ideal ({y_ideal:.4f})",
        zorder=3,
    )
    ax2.axhline(
        y=y_low,
        color=y_tol_color,
        linestyle=":",
        linewidth=1,
        alpha=0.95 if dark_mode else 0.7,
        label=f"y-low ({y_low:.4f})",
        zorder=2,
    )
    ax2.axhline(
        y=y_high,
        color=y_tol_color,
        linestyle=":",
        linewidth=1,
        alpha=0.95 if dark_mode else 0.7,
        label=f"y-high ({y_high:.4f})",
        zorder=2,
    )
    ax2.fill_between(
        full_grayscale,
        y_low,
        y_high,
        alpha=0.22 if dark_mode else 0.15,
        color=y_tol_color,
        zorder=1,
    )

    ax2.set_xlabel("灰阶 (%)", fontsize=9, color=axis_text)
    ax2.set_ylabel("CIE y", fontsize=9, color=axis_text)
    ax2.grid(True, linestyle="--", alpha=0.45 if dark_mode else 0.3, color=grid_color)
    ax2.set_xlim(0, 105)

    # 纵坐标范围由用户参数控制
    y_min_data = min(y_measured)
    y_max_data = max(y_measured)
    data_range_y = y_max_data - y_min_data

    self.log_gui.log(f"  y数据波动: {data_range_y:.6f}", level="info")

    range_span = y_tolerance * 2
    extra_margin = range_span * margin_ratio

    final_y_min = min(y_min_data, y_low) - extra_margin
    final_y_max = max(y_max_data, y_high) + extra_margin

    if y_min_data >= y_low and y_max_data <= y_high:
        self.log_gui.log(f"  y数据在tolerance范围内，使用tolerance范围显示", level="info")
        final_y_min = y_low - extra_margin
        final_y_max = y_high + extra_margin
    else:
        self.log_gui.log(f"  y数据超出tolerance范围，扩展显示范围", level="info")

    ax2.set_ylim(final_y_min, final_y_max)
    self.log_gui.log(
        f"  y轴显示范围: {final_y_min:.6f} - {final_y_max:.6f} (跨度: {final_y_max - final_y_min:.6f})"
    , level="info")

    # ========== 总标题 - 统一格式（去掉统计信息）==========
    test_type_name = self.get_test_type_name(test_type)

    self.cct_fig.suptitle(
        f"{test_type_name} - 色度一致性测试",
        fontsize=12,
        y=0.98,
        fontweight="bold",
        color=palette["fg"],
    )

    self.cct_fig.subplots_adjust(
        left=0.12,
        right=0.82,
        top=0.92,
        bottom=0.08,
        hspace=0.30,
    )

    legend1 = ax1.legend(
        fontsize=7,
        loc="center left",
        bbox_to_anchor=(1.05, 0.5),
        framealpha=0.92 if dark_mode else 1.0,
        facecolor=legend_bg,
        edgecolor=legend_edge,
    )
    legend2 = ax2.legend(
        fontsize=7,
        loc="center left",
        bbox_to_anchor=(1.05, 0.5),
        framealpha=0.92 if dark_mode else 1.0,
        facecolor=legend_bg,
        edgecolor=legend_edge,
    )
    for legend in (legend1, legend2):
        for text in legend.get_texts():
            text.set_color(axis_text)

    self.cct_canvas.draw()
    self.chart_notebook.select(self.cct_chart_frame)

    self.log_gui.log("xy 色度坐标图绘制完成", level="success")


class PlotCctMixin:
    """由 tools/refactor_to_mixins.py 自动生成。
    把本模块的自由函数挂到 PQAutomationApp 上，便于 F12 跳转与类型推断。
    """
    plot_cct = plot_cct
