"""色准测试结果绘制。

布局：
- 左侧：大尺寸 ColorChecker 条形图（每个条形使用对应颜色）。
- 右侧：CIE 1976 u'v' 色度图（目标点/实测点/偏移连线）。
"""

from typing import TYPE_CHECKING

from matplotlib.patches import Rectangle
from matplotlib.lines import Line2D
from matplotlib.ticker import MultipleLocator, AutoMinorLocator
from app.views.modern_styles import get_theme_palette
from app.plots.gamut_background import get_cie1976_background
from app.tests.color_accuracy import get_accuracy_color_standards

if TYPE_CHECKING:
    from pqAutomationApp import PQAutomationApp


# ============================================================
# 常量
# ============================================================

_COLOR_MAP = {
    "White": "#FFFFFF",
    "Gray 80": "#E6E6E6",
    "Gray 65": "#D1D1D1",
    "Gray 50": "#BABABA",
    "Gray 35": "#9E9E9E",
    "Black": "#000000",
    "Dark Skin": "#735242",
    "Light Skin": "#C29682",
    "Blue Sky": "#5E7A9C",
    "Foliage": "#596B42",
    "Blue Flower": "#8280B0",
    "Bluish Green": "#63BDA8",
    "Orange": "#D97829",
    "Purplish Blue": "#4A5CA3",
    "Moderate Red": "#C25461",
    "Purple": "#5C3D6B",
    "Yellow Green": "#9EBA40",
    "Orange Yellow": "#E6A12E",
    "Blue (Legacy)": "#333D96",
    "Green (Legacy)": "#479447",
    "Red (Legacy)": "#B0303B",
    "Yellow (Legacy)": "#EDC721",
    "Magenta (Legacy)": "#BA5491",
    "Cyan (Legacy)": "#0085A3",
    "100% Red": "#FF0000",
    "100% Green": "#00FF00",
    "100% Blue": "#0000FF",
    "100% Cyan": "#00FFFF",
    "100% Magenta": "#FF00FF",
    "100% Yellow": "#FFFF00",
}


def _xy_to_uv(x: float, y: float):
    """CIE 1931 xy → CIE 1976 u'v'"""
    denom = -2.0 * x + 12.0 * y + 3.0
    if abs(denom) < 1e-10:
        return 0.0, 0.0
    return (4.0 * x) / denom, (9.0 * y) / denom

# ============================================================
# 子图：左侧 Calman 风格面板
# ============================================================

def _draw_left_panel(ax, color_patches, delta_e_values, font_scale=1.0, dark_mode=False):
    """左侧仅保留大条形图"""

    ax.clear()

    n = len(color_patches)
    if n == 0:
        ax.set_axis_off()
        return

    y_pos = list(range(n))
    bar_colors = [_COLOR_MAP.get(name, "#888888") for name in color_patches]

    edgecolor = "#F3F5F7" if dark_mode else "#202020"
    text_color = "#F3F5F7" if dark_mode else "#111111"
    bg_color = "#0F1115" if dark_mode else "#FFFFFF"

    ax.barh(
        y_pos,
        delta_e_values,
        height=0.72,
        color=bar_colors,
        edgecolor=edgecolor,
        linewidth=0.5,
        zorder=3,
    )

    ax.set_yticks(y_pos)
    ax.set_yticklabels(color_patches, fontsize=max(5, 7 * font_scale), color=text_color)
    ax.invert_yaxis()

    x_max = max(15.0, max(delta_e_values) * 1.15)
    ax.set_xlim(0, x_max)

    ax.tick_params(
        axis="x",
        labelsize=max(6, 8 * font_scale),
        colors=text_color
    )

    ax.tick_params(
        axis="y",
        labelsize=max(5, 7 * font_scale),
        colors=text_color
    )

    ax.set_facecolor(bg_color)

    # 自动 minor tick
    ax.xaxis.set_minor_locator(AutoMinorLocator(2))


# ============================================================
# 子图：CIE 1976 u'v' 色度图（目标 vs 实测）
# ============================================================

def _draw_uv_diagram(ax, color_patches, measurements, standards, font_scale=1.0, dark_mode=False):
    """绘制 CIE 1976 u'v' 上的色准对比。"""
    ax.clear()
    try:
        bg, bbox = get_cie1976_background(mode="dark" if dark_mode else "light")
        if bg.shape[-1] == 4:
            bg = bg[:, :, :3]
        xmin, xmax, ymin, ymax = bbox
        ax.imshow(
            bg,
            extent=(xmin, xmax, ymin, ymax),
            origin="lower",
            interpolation="bilinear",
            zorder=0,
            aspect="auto",
        )
        ax.set_xlim(xmin, xmax)
        ax.set_ylim(ymin, ymax)
    except Exception:
        ax.set_xlim(0.0, 0.65)
        ax.set_ylim(0.0, 0.60)

    text_color = "#F3F5F7" if dark_mode else "#111111"
    sub_text_color = "#D3D7DD" if dark_mode else "#222222"
    tick_color = "#D3D7DD" if dark_mode else "#222222"
    legend_label_color = "#FFF" if dark_mode else "#111"
    legend_bg = "#111" if dark_mode else "#FFFFFF"
    legend_edge = "#FFF" if dark_mode else "#333"
    outer_edge = "#FFFFFF" if dark_mode else "#222222"

    ax.set_facecolor("#000" if dark_mode else "#FFFFFF")
    ax.set_aspect("equal", adjustable="box")
    ax.xaxis.set_major_locator(MultipleLocator(0.1))
    ax.yaxis.set_major_locator(MultipleLocator(0.1))

    ax.xaxis.set_minor_locator(MultipleLocator(0.02))
    ax.yaxis.set_minor_locator(MultipleLocator(0.02))

    ax.set_title(
        "CIE 1976 u'v'",
        fontsize=max(8, 11 * font_scale),
        fontweight="bold",
        color=text_color,
        pad=4,
    )

    ax.set_xlabel("u'", fontsize=max(7, 9 * font_scale), color=sub_text_color, labelpad=1)
    ax.set_ylabel("v'", fontsize=max(7, 9 * font_scale), color=sub_text_color, labelpad=1)

    ax.tick_params(axis="both", labelsize=max(6, 8 * font_scale), colors=tick_color)

    for sp in ax.spines.values():
        sp.set_color(outer_edge)
        sp.set_linewidth(0.9)

    for name, meas in zip(color_patches, measurements):

        if meas is None or len(meas) < 2:
            continue

        mx, my = meas[0], meas[1]
        sxy = standards.get(name)

        if sxy is None:
            continue

        sx, sy = sxy

        m_u, m_v = _xy_to_uv(mx, my)
        s_u, s_v = _xy_to_uv(sx, sy)

        # face = get_patch_color_from_xy(name, (sx, sy)).strip().upper()
        face = _COLOR_MAP.get(name, "#FFFFFF")
        # face = get_patch_color_from_xy(name, (mx, my))
        # face = "#FF0000"


        # 目标点（Target） 空心方框
        ax.scatter(
            s_u,
            s_v,
            s=90,
            marker="s",
            facecolors="none",
            edgecolors=outer_edge,
            linewidths=1.6,
            zorder=18,
        )

        # 实测点（Actual） 彩色实心 + 白色描边
        ax.scatter(
            [m_u],
            [m_v],
            s=80,
            marker="o",
            color=face,
            edgecolors=outer_edge,
            linewidths=1.2,
            zorder=20,
        )

        # # Δu'v' 偏差连线
        # ax.plot(
        #     [s_u, m_u],
        #     [s_v, m_v],
        #     color=face,
        #     linewidth=1.0,
        #     alpha=0.8,
        #     zorder=15,
        # )

    legend_handles = [
        Line2D(
            [0],
            [0],
            marker="s",
            linestyle="none",
            markerfacecolor="none",
            markeredgecolor=outer_edge,
            markersize=9,
            markeredgewidth=1.4,
            label="目标 (Target)",
        ),
        Line2D(
            [0],
            [0],
            marker="o",
            linestyle="none",
            markerfacecolor="#AAAAAA",
            markeredgecolor=outer_edge,
            markersize=9,
            markeredgewidth=1.2,
            label="实测 (Actual)",
        ),
    ]

    leg = ax.legend(
        handles=legend_handles,
        loc="lower right",
        fontsize=max(6, 8 * font_scale),
        framealpha=0.9,
        labelcolor=legend_label_color,
    )

    if leg:
        leg.get_frame().set_facecolor(legend_bg)
        leg.get_frame().set_edgecolor(legend_edge)
        leg.set_zorder(50)



def _draw_result_judgement(ax, accuracy_data, font_scale=1.0, dark_mode=False):
    """底部结果条"""
    ax.clear()
    ax.set_xlim(0, 1)
    ax.set_ylim(0, 1)
    ax.axis("off")

    avg = accuracy_data.get("avg_delta_e", 0.0)
    mx = accuracy_data.get("max_delta_e", 0.0)

    panel_bg = "#1A1E24" if dark_mode else "#FFFFFF"
    panel_edge = "#4A5058" if dark_mode else "#C6C6C6"
    text_color = "#F3F5F7" if dark_mode else "#111111"

    ax.add_patch(Rectangle(
        (0.0, 0.10), 1.0, 0.80,
        transform=ax.transAxes,
        facecolor=panel_bg, edgecolor=panel_edge, linewidth=1.0,
    ))

    ax.text(
        0.03, 0.50,
        f"Avg dE2000: {avg:.2f}",
        ha="left", va="center",
        fontsize=max(11, 20 * font_scale), fontweight="normal", color=text_color,
        transform=ax.transAxes,
    )
    ax.text(
        0.52, 0.50,
        f"Max dE2000: {mx:.2f}",
        ha="left", va="center",
        fontsize=max(11, 20 * font_scale), fontweight="normal", color=text_color,
        transform=ax.transAxes,
    )


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

def plot_accuracy(self: "PQAutomationApp", accuracy_data, test_type):
    """绘制色准测试结果"""
    palette = get_theme_palette()
    try:
        from app.views.theme_manager import is_dark
        dark_mode = is_dark()
    except Exception:
        dark_mode = False

    fig = self.accuracy_fig
    fig.clear()
    try:
        fig.set_layout_engine(None)
    except Exception:
        try:
            fig.set_tight_layout(False)
        except Exception:
            pass

    fig.patch.set_facecolor(palette["bg"])

    # 先确保色准页签已激活，再读取真实画布尺寸进行动态缩放。
    font_scale = 1.0
    try:
        self.chart_notebook.select(self.accuracy_chart_frame)
        self.chart_notebook.update_idletasks()
        canvas_widget = self.accuracy_canvas.get_tk_widget()
        canvas_widget.update_idletasks()
        canvas_widget.update()
        cw = max(1, int(canvas_widget.winfo_width()))
        ch = max(1, int(canvas_widget.winfo_height()))
        font_scale = min(cw / 1000.0, ch / 600.0)
        font_scale = max(0.60, min(1.0, font_scale))
    except Exception:
        font_scale = 1.0

    color_patches = accuracy_data.get("color_patches", []) or []
    delta_e_values = accuracy_data.get("delta_e_values", []) or []
    measurements = accuracy_data.get("color_measurements", []) or []

    try:
        target_gamma = float(accuracy_data.get("target_gamma", 2.2))
    except (TypeError, ValueError):
        target_gamma = 2.2

    test_type_name = self.get_test_type_name(test_type)

    if test_type == "sdr_movie":
        title = f"{test_type_name} - 色准测试（全 29色 | Gamma {target_gamma}）"
    elif test_type == "hdr_movie":
        title = f"{test_type_name} - 色准测试（全 29色 | PQ EOTF）"
    else:
        title = f"{test_type_name} - 色准测试（全 29色 | Gamma {target_gamma}）"

    fig.suptitle(
        title,
        fontsize=max(8, 11 * font_scale),
        y=0.975,
        fontweight="bold",
        color=palette["fg"],
    )

    gs = fig.add_gridspec(
        2, 2,
        width_ratios=[1.12, 1.0],
        height_ratios=[4.8, 0.48],
        left=0.08, right=0.985,
        top=0.92, bottom=0.05,
        wspace=0.14, hspace=0.08,
    )

    ax_left = fig.add_subplot(gs[0, 0])
    ax_uv = fig.add_subplot(gs[0, 1])
    ax_judge = fig.add_subplot(gs[1, :])
    for ax in (ax_left, ax_uv, ax_judge):
        ax.set_facecolor(palette["card_bg"])

    # 兼容外部对 self.accuracy_ax 的引用
    self.accuracy_ax = ax_judge

    _draw_left_panel(
        ax_left,
        color_patches,
        delta_e_values,
        font_scale=font_scale,
        dark_mode=dark_mode,
    )

    try:
        standards = get_accuracy_color_standards(test_type)
    except Exception:
        standards = {}

    _draw_uv_diagram(
        ax_uv,
        color_patches,
        measurements,
        standards,
        font_scale=font_scale,
        dark_mode=dark_mode,
    )
    _draw_result_judgement(
        ax_judge,
        accuracy_data,
        font_scale=font_scale,
        dark_mode=dark_mode,
    )

    try:
        self.update_accuracy_result_table(accuracy_data, standards)
    except Exception:
        pass

    # 重新刷新布局并绘制，确保画布尺寸与 notebook tab 对齐。
    self.chart_notebook.select(self.accuracy_chart_frame)
    canvas_widget = self.accuracy_canvas.get_tk_widget()
    canvas_widget.update_idletasks()
    canvas_widget.update()
    self.accuracy_canvas.draw()
    canvas_widget.update_idletasks()


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