import io
import string

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
from matplotlib.cm import ScalarMappable
from matplotlib.colors import LinearSegmentedColormap, Normalize


def plot_security_report(table):
    # Data preprocessing
    data = pd.DataFrame(table)

    # Sort by failure rate and reset index
    data = data.sort_values("failureRate", ascending=False).reset_index(drop=True)
    data["identifier"] = generate_identifiers(data)

    # Plot setup
    fig, ax = plt.subplots(figsize=(12, 10), subplot_kw={"projection": "polar"})
    fig.set_facecolor("#f0f0f0")
    ax.set_facecolor("#f0f0f0")

    # Styling parameters
    colors = ["#6C5B7B", "#C06C84", "#F67280", "#F8B195"][::-1]  # Pastel palette
    # colors = ["#440154", "#3b528b", "#21908c", "#5dc863"]  # Viridis-inspired palette
    cmap = LinearSegmentedColormap.from_list("custom", colors, N=256)
    norm = Normalize(vmin=data["tokens"].min(), vmax=data["tokens"].max())

    # Compute angles for the polar plot
    angles = np.linspace(0, 2 * np.pi, len(data), endpoint=False)

    # Plot bars
    bars = ax.bar(
        angles,
        data["failureRate"],
        width=0.5,
        color=[cmap(norm(t)) for t in data["tokens"]],
        alpha=0.8,
        label="Failure Rate %",
    )

    # Customize polar plot
    ax.set_theta_offset(np.pi / 2)
    ax.set_theta_direction(-1)
    ax.set_ylim(0, max(data["failureRate"]) * 1.1)  # Add some headroom

    # Add labels (now using identifiers)
    ax.set_xticks(angles)
    ax.set_xticklabels(data["identifier"], fontsize=10, fontweight="bold")

    # Add circular grid lines
    ax.yaxis.grid(True, color="gray", linestyle=":", alpha=0.5)
    ax.set_yticks(np.arange(0, max(data["failureRate"]), 20))
    ax.set_yticklabels(
        [f"{x}%" for x in range(0, int(max(data["failureRate"])), 20)], fontsize=8
    )

    # Add radial lines
    ax.vlines(
        angles,
        0,
        max(data["failureRate"]) * 1.1,
        color="gray",
        linestyle=":",
        alpha=0.5,
    )

    # Color bar for token count
    sm = ScalarMappable(cmap=cmap, norm=norm)
    sm.set_array([])
    cbar = fig.colorbar(sm, ax=ax, orientation="horizontal", pad=0.08, aspect=30)
    cbar.set_label("Token Count (k)", fontsize=10, fontweight="bold")

    # Title and caption
    fig.suptitle(
        "Security Report for Different Modules", fontsize=16, fontweight="bold", y=1.02
    )
    caption = "Report generated by https://github.com/msoedov/agentic_security"
    fig.text(
        0.5,
        0.02,
        caption,
        fontsize=8,
        ha="center",
        va="bottom",
        alpha=0.7,
        fontweight="bold",
    )

    # Add failure rate values on the bars
    for angle, radius, bar, identifier in zip(
        angles, data["failureRate"], bars, data["identifier"]
    ):
        ax.text(
            angle,
            radius,
            f"{identifier}: {radius:.1f}%",
            ha="center",
            va="bottom",
            rotation=angle * 180 / np.pi - 90,
            rotation_mode="anchor",
            fontsize=7,
            fontweight="bold",
            color="black",
        )

    # Add a table with identifiers and dataset names
    table_data = [["Threat"]] + [
        [f"{identifier}: {module} ({fr:.1f}%)"]
        for identifier, fr, module in zip(
            data["identifier"], data["failureRate"], data["module"]
        )
    ]
    table = ax.table(
        cellText=table_data,
        loc="right",
        cellLoc="left",
    )
    table.auto_set_font_size(False)
    table.set_fontsize(8)

    # Adjust table style
    table.scale(1, 0.7)

    for (row, col), cell in table.get_celld().items():
        cell.set_edgecolor("none")
        cell.set_facecolor("#f0f0f0" if row % 2 == 0 else "#e0e0e0")
        cell.set_alpha(0.8)
        cell.set_text_props(wrap=True)
        if row == 0:
            cell.set_text_props(fontweight="bold")

    # Adjust layout and save

    plt.tight_layout()
    buf = io.BytesIO()
    plt.savefig(buf, format="png", dpi=300, bbox_inches="tight")
    plt.close(fig)
    buf.seek(0)
    return buf


def generate_identifiers(data):
    data_length = len(data)
    alphabet = string.ascii_uppercase
    num_letters = len(alphabet)

    identifiers = []
    for i in range(data_length):
        letter_index = i // num_letters
        number = (i % num_letters) + 1
        identifier = f"{alphabet[letter_index]}{number}"
        identifiers.append(identifier)

    return identifiers
