#!/usr/bin/env python3
"""Generate diagrams for PYTANIE 31: Interaktywne wspomaganie decyzji w warunkach ryzyka.

Diagrams:
  1. Payoff matrix + all criteria results comparison (bar chart)
  2. Regret matrix construction step-by-step
  3. Hurwicz \u03b1 interpolation between maximax and maximin
  4. Decision criteria mnemonic map
  5. Expected value criterion with probability-weighted bars
  6. Decision conditions spectrum (pewność → ryzyko → niepewność)

All: A4-compatible, B&W, 300 DPI, laser-printer-friendly.
"""

import matplotlib as mpl

mpl.use("Agg")
from pathlib import Path

import matplotlib.patches as mpatches
from matplotlib.patches import FancyBboxPatch
import matplotlib.pyplot as plt
import numpy as np

DPI = 300
BG = "white"
LN = "black"
FS = 8
FS_TITLE = 11
OUTPUT_DIR = str(Path(__file__).resolve().parent / "img")
Path(OUTPUT_DIR).mkdir(parents=True, exist_ok=True)

GRAY1 = "#E8E8E8"
GRAY2 = "#D0D0D0"
GRAY3 = "#B8B8B8"
GRAY4 = "#F5F5F5"
GRAY5 = "#C0C0C0"


def draw_box(
    ax,
    x,
    y,
    w,
    h,
    text,
    fill="white",
    lw=1.2,
    fontsize=FS,
    fontweight="normal",
    ha="center",
    va="center",
    rounded=True,
) -> None:
    """Draw box."""
    if rounded:
        rect = FancyBboxPatch(
            (x, y), w, h, boxstyle="round,pad=0.05", lw=lw, edgecolor=LN, facecolor=fill
        )
    else:
        rect = mpatches.Rectangle((x, y), w, h, lw=lw, edgecolor=LN, facecolor=fill)
    ax.add_patch(rect)
    ax.text(
        x + w / 2,
        y + h / 2,
        text,
        ha=ha,
        va=va,
        fontsize=fontsize,
        fontweight=fontweight,
        wrap=True,
    )


def draw_arrow(ax, x1, y1, x2, y2, lw=1.2, style="->", color=LN) -> None:
    """Draw arrow."""
    ax.annotate(
        "",
        xy=(x2, y2),
        xytext=(x1, y1),
        arrowprops={"arrowstyle": style, "color": color, "lw": lw},
    )


# ============================================================
# 1. PAYOFF MATRIX + ALL CRITERIA BAR CHART
# ============================================================
def draw_criteria_comparison() -> None:
    """Draw criteria comparison."""
    fig, axes = plt.subplots(
        1, 2, figsize=(8.27, 4.5), gridspec_kw={"width_ratios": [1.2, 1]}
    )

    # -- Left: Payoff matrix as styled table --
    ax = axes[0]
    ax.axis("off")
    ax.set_xlim(0, 6)
    ax.set_ylim(0, 6)
    ax.set_title(
        "Macierz wypłat (tys. zł)", fontsize=FS_TITLE, fontweight="bold", pad=8
    )

    # Headers
    headers_col = ["", "S₁\n(dobra)", "S₂\n(średnia)", "S₃\n(zła)"]
    rows = [
        ["A₁ (fabryka)", "200", "50", "-100"],
        ["A₂ (sklep)", "80", "70", "40"],
        ["A₃ (obligacje)", "30", "30", "30"],
    ]

    col_w = [1.8, 1.2, 1.2, 1.2]
    row_h = 0.7
    start_y = 4.5
    start_x = 0.2

    # Draw header row
    x = start_x
    for j, h in enumerate(headers_col):
        fill = GRAY2 if j > 0 else GRAY3
        rect = mpatches.Rectangle(
            (x, start_y), col_w[j], row_h, lw=1, edgecolor=LN, facecolor=fill
        )
        ax.add_patch(rect)
        ax.text(
            x + col_w[j] / 2,
            start_y + row_h / 2,
            h,
            ha="center",
            va="center",
            fontsize=FS,
            fontweight="bold",
        )
        x += col_w[j]

    # Draw data rows
    for i, row in enumerate(rows):
        x = start_x
        y = start_y - (i + 1) * row_h
        for j, val in enumerate(row):
            fill = GRAY4 if j == 0 else ("white" if i % 2 == 0 else GRAY1)
            # Highlight negative
            if val.startswith("-"):
                fill = "#D8D8D8"
            rect = mpatches.Rectangle(
                (x, y), col_w[j], row_h, lw=1, edgecolor=LN, facecolor=fill
            )
            ax.add_patch(rect)
            fw = "bold" if j == 0 else "normal"
            ax.text(
                x + col_w[j] / 2,
                y + row_h / 2,
                val,
                ha="center",
                va="center",
                fontsize=FS,
                fontweight=fw,
            )
            x += col_w[j]

    # Probability row for EV
    x = start_x
    y = start_y - 4 * row_h
    probs = ["p (dla E[X]):", "0.5", "0.3", "0.2"]
    for j, val in enumerate(probs):
        fill = GRAY5 if j > 0 else GRAY3
        rect = mpatches.Rectangle(
            (x, y), col_w[j], row_h * 0.7, lw=1, edgecolor=LN, facecolor=fill
        )
        ax.add_patch(rect)
        ax.text(
            x + col_w[j] / 2,
            y + row_h * 0.35,
            val,
            ha="center",
            va="center",
            fontsize=7,
            fontweight="bold",
            style="italic",
        )
        x += col_w[j]

    # -- Right: Bar chart comparing criteria results --
    ax2 = axes[1]
    criteria = [
        "E[X]",
        "Laplace",
        "Maximax",
        "Maximin",
        "Hurwicz\n\u03b1=0.6",
        "Savage",
    ]

    # Recalculate with probabilities 0.5, 0.3, 0.2
    # E[X]: A1=200*0.5+50*0.3+(-100)*0.2=100+15-20=95
    #        A2=80*0.5+70*0.3+40*0.2=40+21+8=69
    #        A3=30*0.5+30*0.3+30*0.2=15+9+6=30
    ev = [95, 69, 30]
    laplace = [50, 63.3, 30]
    maximax = [200, 80, 30]
    maximin = [-100, 40, 30]
    hurwicz = [80, 64, 30]  # alpha=0.6
    savage_maxregret = [140, 120, 170]  # lower = better

    # Which alternative wins for each criterion?
    winners = [0, 1, 0, 1, 0, 1]  # index of winning alternative

    # Display as grouped bar chart - each criterion shows the 3 alternatives
    x_pos = np.arange(len(criteria))
    width = 0.22
    hatches = ["///", "...", "xxx"]
    labels = ["A₁ (fabryka)", "A₂ (sklep)", "A₃ (obligacje)"]

    all_vals = [
        [ev[0], laplace[0], maximax[0], maximin[0], hurwicz[0], savage_maxregret[0]],
        [ev[1], laplace[1], maximax[1], maximin[1], hurwicz[1], savage_maxregret[1]],
        [ev[2], laplace[2], maximax[2], maximin[2], hurwicz[2], savage_maxregret[2]],
    ]

    for i in range(3):
        ax2.bar(
            x_pos + (i - 1) * width,
            all_vals[i],
            width,
            label=labels[i],
            color="white",
            edgecolor=LN,
            hatch=hatches[i],
            lw=0.8,
        )

    # Mark winners with star
    for c_idx in range(len(criteria)):
        w = winners[c_idx]
        val = all_vals[w][c_idx]
        ax2.text(
            x_pos[c_idx] + (w - 1) * width,
            val + 5,
            "★",
            ha="center",
            va="bottom",
            fontsize=10,
            fontweight="bold",
        )

    ax2.set_xticks(x_pos)
    ax2.set_xticklabels(criteria, fontsize=7)
    ax2.set_ylabel("Wartość kryterium", fontsize=8)
    ax2.set_title("Porównanie kryteriów", fontsize=FS_TITLE, fontweight="bold", pad=8)
    ax2.legend(fontsize=7, loc="upper right")
    ax2.axhline(y=0, color=LN, lw=0.5, ls="-")
    ax2.spines["top"].set_visible(False)
    ax2.spines["right"].set_visible(False)
    ax2.tick_params(labelsize=7)

    # Note about Savage
    ax2.text(
        5,
        -30,
        "(Savage: niżej\n= lepiej)",
        fontsize=6,
        ha="center",
        va="top",
        style="italic",
    )

    plt.tight_layout()
    outpath = str(Path(OUTPUT_DIR) / "q31_criteria_comparison.png")
    fig.savefig(outpath, dpi=DPI, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    print(f"  Saved: {outpath}")


# ============================================================
# 2. REGRET MATRIX CONSTRUCTION
# ============================================================
def draw_regret_matrix() -> None:
    """Draw regret matrix."""
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 5))
    ax.axis("off")
    ax.set_xlim(0, 10)
    ax.set_ylim(0, 7)
    ax.set_title(
        "Kryterium Savage'a — budowa macierzy żalu",
        fontsize=FS_TITLE + 1,
        fontweight="bold",
        pad=10,
    )

    # --- Step 1: Original payoff matrix (left) ---
    ax.text(
        2.2,
        6.3,
        "Krok 1: Macierz wypłat",
        fontsize=9,
        fontweight="bold",
        ha="center",
        va="center",
    )

    col_w = 1.0
    row_h = 0.55
    headers = ["", "S₁", "S₂", "S₃"]
    data = [
        ["A₁", "200", "50", "-100"],
        ["A₂", "80", "70", "40"],
        ["A₃", "30", "30", "30"],
    ]
    start_x = 0.3
    start_y = 5.5

    for j, h in enumerate(headers):
        w = 0.7 if j == 0 else col_w
        x = start_x + (0 if j == 0 else 0.7 + (j - 1) * col_w)
        rect = mpatches.Rectangle(
            (x, start_y), w, row_h, lw=1, edgecolor=LN, facecolor=GRAY2
        )
        ax.add_patch(rect)
        ax.text(
            x + w / 2,
            start_y + row_h / 2,
            h,
            ha="center",
            va="center",
            fontsize=FS,
            fontweight="bold",
        )

    for i, row in enumerate(data):
        y = start_y - (i + 1) * row_h
        for j, val in enumerate(row):
            w = 0.7 if j == 0 else col_w
            x = start_x + (0 if j == 0 else 0.7 + (j - 1) * col_w)
            fill = GRAY4 if j == 0 else "white"
            rect = mpatches.Rectangle(
                (x, y), w, row_h, lw=1, edgecolor=LN, facecolor=fill
            )
            ax.add_patch(rect)
            ax.text(
                x + w / 2, y + row_h / 2, val, ha="center", va="center", fontsize=FS
            )

    # Max per column annotation
    max_y = start_y - 3 * row_h - 0.1
    ax.text(
        start_x + 0.7 + 0.5 * col_w,
        max_y,
        "max=200",
        fontsize=7,
        ha="center",
        va="top",
        fontweight="bold",
        color="#333",
    )
    ax.text(
        start_x + 0.7 + 1.5 * col_w,
        max_y,
        "max=70",
        fontsize=7,
        ha="center",
        va="top",
        fontweight="bold",
        color="#333",
    )
    ax.text(
        start_x + 0.7 + 2.5 * col_w,
        max_y,
        "max=40",
        fontsize=7,
        ha="center",
        va="top",
        fontweight="bold",
        color="#333",
    )

    # Arrow
    ax.annotate(
        "",
        xy=(5.0, 4.8),
        xytext=(4.2, 4.8),
        arrowprops={"arrowstyle": "->", "color": LN, "lw": 2},
    )
    ax.text(
        4.6,
        5.0,
        "rᵢⱼ = max - aᵢⱼ",
        fontsize=8,
        ha="center",
        va="bottom",
        fontweight="bold",
    )

    # --- Step 2: Regret matrix (right) ---
    ax.text(
        7.5,
        6.3,
        "Krok 2: Macierz żalu",
        fontsize=9,
        fontweight="bold",
        ha="center",
        va="center",
    )

    regret_data = [
        ["A₁", "0", "20", "140"],
        ["A₂", "120", "0", "0"],
        ["A₃", "170", "40", "10"],
    ]
    headers2 = ["", "S₁", "S₂", "S₃", "max rᵢ"]
    start_x2 = 5.3

    for j, h in enumerate(headers2):
        w = 0.7 if j == 0 else (0.9 if j < 4 else 1.0)
        x = start_x2
        if j == 0:
            x = start_x2
        elif j <= 3:
            x = start_x2 + 0.7 + (j - 1) * 0.9
        else:
            x = start_x2 + 0.7 + 3 * 0.9
        rect = mpatches.Rectangle(
            (x, start_y),
            w,
            row_h,
            lw=1,
            edgecolor=LN,
            facecolor=GRAY2 if j < 4 else GRAY3,
        )
        ax.add_patch(rect)
        ax.text(
            x + w / 2,
            start_y + row_h / 2,
            h,
            ha="center",
            va="center",
            fontsize=FS,
            fontweight="bold",
        )

    max_regrets = [140, 120, 170]
    for i, row in enumerate(regret_data):
        y = start_y - (i + 1) * row_h
        for j, val in enumerate(row):
            w = 0.7 if j == 0 else 0.9
            x = start_x2 + (0 if j == 0 else 0.7 + (j - 1) * 0.9)
            fill = GRAY4 if j == 0 else "white"
            # Highlight the max regret cell
            if j > 0 and int(val) == max_regrets[i]:
                fill = GRAY2
            rect = mpatches.Rectangle(
                (x, y), w, row_h, lw=1, edgecolor=LN, facecolor=fill
            )
            ax.add_patch(rect)
            fw = "bold" if (j > 0 and int(val) == max_regrets[i]) else "normal"
            ax.text(
                x + w / 2,
                y + row_h / 2,
                val,
                ha="center",
                va="center",
                fontsize=FS,
                fontweight=fw,
            )

        # Max regret column
        x = start_x2 + 0.7 + 3 * 0.9
        w = 1.0
        fill = "#C8C8C8" if max_regrets[i] == min(max_regrets) else GRAY1
        rect = mpatches.Rectangle(
            (x, y),
            w,
            row_h,
            lw=1.5 if max_regrets[i] == min(max_regrets) else 1,
            edgecolor=LN,
            facecolor=fill,
        )
        ax.add_patch(rect)
        marker = " ★" if max_regrets[i] == min(max_regrets) else ""
        ax.text(
            x + w / 2,
            y + row_h / 2,
            f"{max_regrets[i]}{marker}",
            ha="center",
            va="center",
            fontsize=FS,
            fontweight="bold",
        )

    # Bottom conclusion
    ax.text(
        5.0,
        2.8,
        "Krok 3: Wybierz min z max żalu → A₂ (max żal = 120)",
        fontsize=10,
        ha="center",
        va="center",
        fontweight="bold",
        bbox={
            "boxstyle": "round,pad=0.3",
            "facecolor": GRAY1,
            "edgecolor": LN,
            "lw": 1.5,
        },
    )

    # Interpretatation examples
    ax.text(
        5.0,
        2.0,
        "Interpretacja żalu: r₁₃ = 140 oznacza:\n"
        "„Gdyby nastąpił S₃ (zła koniunktura), a wybrałbym A₁,\n"
        'żałowałbym, bo najlepszą opcją byłoby A₂ z wynikiem 40 — traciłbym 140"',
        fontsize=7.5,
        ha="center",
        va="center",
        style="italic",
        bbox={
            "boxstyle": "round,pad=0.3",
            "facecolor": GRAY4,
            "edgecolor": GRAY3,
            "lw": 0.8,
        },
    )

    # Mnemonic
    ax.text(
        5.0,
        0.8,
        'Mnemonik: Savage = „Żal jak nóż"\nMaksymalny żal to nóż '
        "— wybierz opcję z NAJMNIEJSZYM nożem",
        fontsize=8,
        ha="center",
        va="center",
        fontweight="bold",
        bbox={
            "boxstyle": "round,pad=0.3",
            "facecolor": "white",
            "edgecolor": LN,
            "lw": 1,
        },
    )

    plt.tight_layout()
    outpath = str(Path(OUTPUT_DIR) / "q31_regret_matrix.png")
    fig.savefig(outpath, dpi=DPI, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    print(f"  Saved: {outpath}")


# ============================================================
# 3. HURWICZ alpha INTERPOLATION
# ============================================================
def draw_hurwicz_interpolation() -> None:
    """Draw hurwicz interpolation."""
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 4))
    ax.set_title(
        "Kryterium Hurwicza — wpływ \u03b1 na wybór alternatywy",
        fontsize=FS_TITLE + 1,
        fontweight="bold",
        pad=10,
    )

    alphas = np.linspace(0, 1, 200)

    # V(Ai) = alpha * max_i + (1-alpha) * min_i
    # A1: max=200, min=-100
    # A2: max=80, min=40
    # A3: max=30, min=30
    v1 = alphas * 200 + (1 - alphas) * (-100)
    v2 = alphas * 80 + (1 - alphas) * 40
    v3 = alphas * 30 + (1 - alphas) * 30

    ax.plot(alphas, v1, "k-", lw=2, label="A₁ (fabryka): V = 300\u03b1 - 100")
    ax.plot(alphas, v2, "k--", lw=2, label="A₂ (sklep): V = 40\u03b1 + 40")
    ax.plot(alphas, v3, "k:", lw=2, label="A₃ (obligacje): V = 30")

    # Find crossover points
    # A2 = A1: 40alpha + 40 = 300alpha - 100  →  140 = 260alpha  →  alpha = 140/260 ≈ 0.538
    alpha_cross_12 = 140 / 260
    v_cross_12 = 40 * alpha_cross_12 + 40

    # A2 = A3: 40alpha + 40 = 30  →  40alpha = -10  →  alpha = -0.25 (never — A2 always > A3)
    # A1 = A3: 300alpha - 100 = 30  →  300alpha = 130  →  alpha = 130/300 ≈ 0.433

    ax.plot(alpha_cross_12, v_cross_12, "ko", markersize=8, zorder=5)
    ax.annotate(
        f"\u03b1 ≈ {alpha_cross_12:.2f}\nA₁ = A₂",
        xy=(alpha_cross_12, v_cross_12),
        xytext=(alpha_cross_12 + 0.12, v_cross_12 - 30),
        fontsize=8,
        fontweight="bold",
        arrowprops={"arrowstyle": "->", "color": LN, "lw": 1},
    )

    # Shade winning regions
    ax.axvspan(0, alpha_cross_12, alpha=0.08, color="black", label="_")
    ax.axvspan(alpha_cross_12, 1, alpha=0.15, color="black", label="_")

    ax.text(
        alpha_cross_12 / 2,
        -60,
        "A₂ wygrywa\n(pesymistycznie)",
        fontsize=8,
        ha="center",
        va="center",
        bbox={"boxstyle": "round", "facecolor": "white", "edgecolor": LN},
    )
    ax.text(
        (alpha_cross_12 + 1) / 2,
        160,
        "A₁ wygrywa\n(optymistycznie)",
        fontsize=8,
        ha="center",
        va="center",
        bbox={"boxstyle": "round", "facecolor": "white", "edgecolor": LN},
    )

    # Special alpha values
    ax.axvline(x=0, color=LN, lw=0.5, ls=":")
    ax.axvline(x=1, color=LN, lw=0.5, ls=":")
    ax.text(
        0,
        -115,
        "\u03b1=0\nmaximin",
        fontsize=7,
        ha="center",
        va="top",
        fontweight="bold",
    )
    ax.text(
        1,
        -115,
        "\u03b1=1\nmaximax",
        fontsize=7,
        ha="center",
        va="top",
        fontweight="bold",
    )

    ax.set_xlabel("Współczynnik optymizmu \u03b1", fontsize=9)
    ax.set_ylabel("V(Aᵢ) = \u03b1·max + (1-\u03b1)·min", fontsize=9)
    ax.legend(fontsize=8, loc="upper left")
    ax.spines["top"].set_visible(False)
    ax.spines["right"].set_visible(False)
    ax.set_xlim(-0.05, 1.05)
    ax.axhline(y=0, color=LN, lw=0.3, ls="-")
    ax.tick_params(labelsize=8)

    plt.tight_layout()
    outpath = str(Path(OUTPUT_DIR) / "q31_hurwicz_alpha.png")
    fig.savefig(outpath, dpi=DPI, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    print(f"  Saved: {outpath}")


# ============================================================
# 4. DECISION CRITERIA MNEMONIC MAP
# ============================================================
def draw_criteria_mnemonic() -> None:
    """Draw criteria mnemonic."""
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 6))
    ax.set_xlim(0, 10)
    ax.set_ylim(0, 8)
    ax.set_aspect("equal")
    ax.axis("off")
    ax.set_title(
        "Mapa mnemoniczna — 6 kryteriów decyzyjnych",
        fontsize=FS_TITLE + 2,
        fontweight="bold",
        pad=10,
    )

    # Central node
    draw_box(
        ax,
        3.5,
        3.5,
        3,
        1,
        "MACIERZ\nWYPŁAT",
        fill=GRAY2,
        lw=2,
        fontsize=11,
        fontweight="bold",
    )

    # Criteria boxes around the center
    criteria = [
        # (x, y, w, h, title, mnemonic, formula)
        (
            0,
            6.5,
            3,
            1.2,
            "WARTOŚĆ OCZEKIWANA",
            '„Mam prawdopodobieństwa"',
            "E[Aᵢ] = Σ pⱼ·aᵢⱼ",
        ),
        (3.5, 6.5, 3, 1.2, "LAPLACE", '„Wszystko po równo"', "V = Σaᵢⱼ / n"),
        (7, 6.5, 3, 1.2, "MAXIMAX", '„Optymista: max z max"', "max maxⱼ aᵢⱼ"),
        (0, 0.5, 3, 1.2, "MAXIMIN (Wald)", '„Pesymista: max z min"', "max minⱼ aᵢⱼ"),
        (
            3.5,
            0.5,
            3,
            1.2,
            "HURWICZ",
            '„\u03b1 pomiędzy"',
            "\u03b1·max + (1-\u03b1)·min",
        ),
        (7, 0.5, 3, 1.2, "SAVAGE", '„Min max żalu"', "min maxⱼ rᵢⱼ"),
    ]

    fills = [GRAY3, GRAY1, "white", "white", GRAY1, GRAY3]

    for i, (x, y, w, h, title, mnem, formula) in enumerate(criteria):
        rect = FancyBboxPatch(
            (x, y),
            w,
            h,
            boxstyle="round,pad=0.08",
            lw=1.5,
            edgecolor=LN,
            facecolor=fills[i],
        )
        ax.add_patch(rect)
        ax.text(
            x + w / 2,
            y + h * 0.78,
            title,
            ha="center",
            va="center",
            fontsize=8,
            fontweight="bold",
        )
        ax.text(
            x + w / 2,
            y + h * 0.45,
            mnem,
            ha="center",
            va="center",
            fontsize=7,
            style="italic",
        )
        ax.text(
            x + w / 2,
            y + h * 0.15,
            formula,
            ha="center",
            va="center",
            fontsize=7,
            fontweight="bold",
            family="monospace",
        )

        # Arrows from center to each box
        cx, cy = 5, 4  # center of macierz
        bx, by = x + w / 2, y + h / 2
        if by > cy:
            ax.annotate(
                "",
                xy=(bx, y),
                xytext=(cx, 4.5),
                arrowprops={
                    "arrowstyle": "->",
                    "color": LN,
                    "lw": 1,
                    "connectionstyle": "arc3,rad=0",
                },
            )
        else:
            ax.annotate(
                "",
                xy=(bx, y + h),
                xytext=(cx, 3.5),
                arrowprops={
                    "arrowstyle": "->",
                    "color": LN,
                    "lw": 1,
                    "connectionstyle": "arc3,rad=0",
                },
            )

    # Labels on arrows
    ax.text(
        1.2,
        5.6,
        "znane p",
        fontsize=7,
        ha="center",
        va="center",
        bbox={
            "boxstyle": "round,pad=0.15",
            "facecolor": "white",
            "edgecolor": GRAY3,
            "lw": 0.5,
        },
    )
    ax.text(
        5,
        5.6,
        "p = 1/n",
        fontsize=7,
        ha="center",
        va="center",
        bbox={
            "boxstyle": "round,pad=0.15",
            "facecolor": "white",
            "edgecolor": GRAY3,
            "lw": 0.5,
        },
    )
    ax.text(
        8.7,
        5.6,
        "max ↑",
        fontsize=7,
        ha="center",
        va="center",
        bbox={
            "boxstyle": "round,pad=0.15",
            "facecolor": "white",
            "edgecolor": GRAY3,
            "lw": 0.5,
        },
    )
    ax.text(
        1.2,
        2.5,
        "min ↑",
        fontsize=7,
        ha="center",
        va="center",
        bbox={
            "boxstyle": "round,pad=0.15",
            "facecolor": "white",
            "edgecolor": GRAY3,
            "lw": 0.5,
        },
    )
    ax.text(
        5,
        2.5,
        "podaj \u03b1",
        fontsize=7,
        ha="center",
        va="center",
        bbox={
            "boxstyle": "round,pad=0.15",
            "facecolor": "white",
            "edgecolor": GRAY3,
            "lw": 0.5,
        },
    )
    ax.text(
        8.7,
        2.5,
        "macierz\nżalu",
        fontsize=7,
        ha="center",
        va="center",
        bbox={
            "boxstyle": "round,pad=0.15",
            "facecolor": "white",
            "edgecolor": GRAY3,
            "lw": 0.5,
        },
    )

    plt.tight_layout()
    outpath = str(Path(OUTPUT_DIR) / "q31_criteria_mnemonic.png")
    fig.savefig(outpath, dpi=DPI, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    print(f"  Saved: {outpath}")


# ============================================================
# 5. EXPECTED VALUE CRITERION WITH PROBABILITY BARS
# ============================================================
def draw_expected_value() -> None:
    """Draw expected value."""
    fig, axes = plt.subplots(1, 3, figsize=(8.27, 3.5), sharey=True)
    fig.suptitle(
        "Kryterium wartości oczekiwanej E[X] — rozkład wyników per alternatywa",
        fontsize=FS_TITLE,
        fontweight="bold",
        y=1.02,
    )

    # Probabilities: p1=0.5, p2=0.3, p3=0.2
    probs = [0.5, 0.3, 0.2]
    alts = [
        ("A₁ (fabryka)", [200, 50, -100], 95),
        ("A₂ (sklep)", [80, 70, 40], 69),
        ("A₃ (obligacje)", [30, 30, 30], 30),
    ]

    hatches = ["///", "...", "xxx"]

    for _idx, (ax, (name, vals, ev)) in enumerate(zip(axes, alts, strict=False)):
        # Bar: height = payoff, width proportional to probability
        x_positions = [0, 0.6, 1.0]
        widths = [p * 0.9 for p in probs]

        for i, (v, p, h) in enumerate(zip(vals, probs, hatches, strict=False)):
            color = "white" if v >= 0 else GRAY2
            ax.bar(
                x_positions[i],
                v,
                width=widths[i],
                color=color,
                edgecolor=LN,
                hatch=h,
                lw=0.8,
                align="edge",
            )
            # Value label
            offset = 8 if v >= 0 else -12
            ax.text(
                x_positions[i] + widths[i] / 2,
                v + offset,
                f"{v}",
                ha="center",
                va="center",
                fontsize=8,
                fontweight="bold",
            )
            # Probability contribution
            contrib = v * p
            ax.text(
                x_positions[i] + widths[i] / 2,
                v / 2,
                f"{v}x{p}\n={contrib:.0f}",
                ha="center",
                va="center",
                fontsize=6,
                style="italic",
            )

        # Expected value line
        ax.axhline(y=ev, color=LN, lw=2, ls="--")
        ax.text(
            1.35,
            ev,
            f"E[X]={ev}",
            fontsize=8,
            fontweight="bold",
            va="center",
            ha="left",
            bbox={"boxstyle": "round,pad=0.15", "facecolor": GRAY1, "edgecolor": LN},
        )

        ax.set_title(name, fontsize=9, fontweight="bold")
        ax.set_xticks([0.225, 0.735, 1.09])
        ax.set_xticklabels(["S₁", "S₂", "S₃"], fontsize=7)
        ax.axhline(y=0, color=LN, lw=0.5)
        ax.spines["top"].set_visible(False)
        ax.spines["right"].set_visible(False)
        ax.tick_params(labelsize=7)

        # Star on winner
        if ev == 95:
            ax.text(
                0.7,
                ev + 20,
                "★ MAX",
                fontsize=9,
                fontweight="bold",
                ha="center",
                va="bottom",
            )

    axes[0].set_ylabel("Wypłata (tys. zł)", fontsize=8)

    plt.tight_layout()
    outpath = str(Path(OUTPUT_DIR) / "q31_expected_value.png")
    fig.savefig(outpath, dpi=DPI, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    print(f"  Saved: {outpath}")


# ============================================================
# 6. DECISION CONDITIONS SPECTRUM
# ============================================================
def draw_conditions_spectrum() -> None:
    """Draw conditions spectrum."""
    fig, ax = plt.subplots(1, 1, figsize=(8.27, 3.5))
    ax.set_xlim(0, 10)
    ax.set_ylim(0, 5)
    ax.set_aspect("equal")
    ax.axis("off")
    ax.set_title(
        "Warunki decyzyjne — spektrum wiedzy decydenta",
        fontsize=FS_TITLE + 1,
        fontweight="bold",
        pad=10,
    )

    # Three zones
    zones = [
        (
            0.3,
            1.5,
            2.8,
            2.5,
            "PEWNOŚĆ",
            "white",
            [
                "Znamy dokładny wynik",
                "Przykład: lokata 5%",
                "Metoda: po prostu wybierz",
                "najlepszy wynik",
            ],
        ),
        (
            3.5,
            1.5,
            2.8,
            2.5,
            "RYZYKO",
            GRAY1,
            [
                "Znamy wyniki I prawdop.",
                "Przykład: gra w kości",
                "Metoda: wartość",
                "oczekiwana E[X]",
            ],
        ),
        (
            6.7,
            1.5,
            2.8,
            2.5,
            "NIEPEWNOŚĆ",
            GRAY3,
            [
                "Znamy wyniki, ale",
                "NIE znamy prawdop.",
                "Metody: Laplace, maximax,",
                "maximin, Hurwicz, Savage",
            ],
        ),
    ]

    for x, y, w, h, title, fill, lines in zones:
        rect = FancyBboxPatch(
            (x, y), w, h, boxstyle="round,pad=0.1", lw=2, edgecolor=LN, facecolor=fill
        )
        ax.add_patch(rect)
        ax.text(
            x + w / 2,
            y + h - 0.3,
            title,
            ha="center",
            va="center",
            fontsize=11,
            fontweight="bold",
        )
        for i, line in enumerate(lines):
            ax.text(
                x + w / 2,
                y + h - 0.7 - i * 0.4,
                line,
                ha="center",
                va="center",
                fontsize=7,
            )

    # Arrows between zones
    ax.annotate(
        "",
        xy=(3.4, 2.75),
        xytext=(3.15, 2.75),
        arrowprops={"arrowstyle": "->", "color": LN, "lw": 2},
    )
    ax.annotate(
        "",
        xy=(6.6, 2.75),
        xytext=(6.35, 2.75),
        arrowprops={"arrowstyle": "->", "color": LN, "lw": 2},
    )

    # Bottom: knowledge gradient bar
    gradient_y = 0.5
    gradient_h = 0.5
    n_steps = 50
    for i in range(n_steps):
        x = 0.3 + i * (9.2 / n_steps)
        w = 9.2 / n_steps + 0.01
        gray_val = 1 - (i / n_steps) * 0.7
        rect = mpatches.Rectangle(
            (x, gradient_y), w, gradient_h, lw=0, facecolor=str(gray_val)
        )
        ax.add_patch(rect)

    rect = mpatches.Rectangle(
        (0.3, gradient_y), 9.2, gradient_h, lw=1.5, edgecolor=LN, facecolor="none"
    )
    ax.add_patch(rect)

    ax.text(0.3, gradient_y - 0.15, "Dużo wiedzy", fontsize=7, ha="left", va="top")
    ax.text(9.5, gradient_y - 0.15, "Mało wiedzy", fontsize=7, ha="right", va="top")
    ax.text(
        4.95,
        gradient_y + gradient_h / 2,
        "POZIOM WIEDZY DECYDENTA",
        fontsize=8,
        fontweight="bold",
        ha="center",
        va="center",
        color="white",
    )

    plt.tight_layout()
    outpath = str(Path(OUTPUT_DIR) / "q31_conditions_spectrum.png")
    fig.savefig(outpath, dpi=DPI, bbox_inches="tight", facecolor=BG)
    plt.close(fig)
    print(f"  Saved: {outpath}")


# ============================================================
# MAIN
# ============================================================
if __name__ == "__main__":
    print("Generating PYTANIE 31 diagrams...")
    draw_criteria_comparison()
    draw_regret_matrix()
    draw_hurwicz_interpolation()
    draw_criteria_mnemonic()
    draw_expected_value()
    draw_conditions_spectrum()
    print("Done! All Q31 diagrams saved to:", OUTPUT_DIR)
