"""Structure visualization for binary analysis results."""

from __future__ import annotations

from pathlib import Path
from typing import TYPE_CHECKING, ClassVar

import matplotlib.patches as mpatches
import matplotlib.pyplot as plt

if TYPE_CHECKING:
    from matplotlib.axes import Axes

    from oscura.analyzers.binary.core.results import BinaryAnalysisResult


class StructureVisualizer:
    """Create visual representations of binary file structure.

    Generates diagrams showing message layout, field boundaries,
    and field types with color coding.

    Example:
        >>> visualizer = StructureVisualizer()
        >>> visualizer.visualize_structure(analysis_result, "structure.png")
    """

    # Field type color map
    FIELD_COLORS: ClassVar[dict[str, str]] = {
        "CONSTANT": "#ff6b6b",  # Red
        "SEQUENCE": "#4ecdc4",  # Cyan
        "TIMESTAMP": "#45b7d1",  # Blue
        "CHECKSUM": "#96ceb4",  # Green
        "LENGTH": "#ffeaa7",  # Yellow
        "PAYLOAD": "#f0f0f0",  # Light gray
        "UNKNOWN": "#dfe6e9",  # Gray
    }

    def visualize_structure(self, result: BinaryAnalysisResult, output_path: str | Path) -> None:
        """Create structure visualization.

        Args:
            result: Binary analysis result.
            output_path: Path to save visualization.

        Example:
            >>> visualizer = StructureVisualizer()
            >>> visualizer.visualize_structure(results, "output.png")
        """
        if not result.structure or not result.structure.has_messages:
            # Create simple "No Structure" message
            fig, ax = plt.subplots(figsize=(10, 2))
            ax.text(
                0.5,
                0.5,
                "No Message Structure Detected",
                ha="center",
                va="center",
                fontsize=16,
            )
            ax.axis("off")
            plt.savefig(output_path, dpi=150, bbox_inches="tight")
            plt.close()
            return

        # Create figure with message layout diagram
        fig, ax = plt.subplots(figsize=(14, 8))

        # Draw message structure
        self._draw_message_layout(ax, result)

        msg_len = result.structure.message_length or 100
        ax.set_xlim(-0.5, msg_len + 0.5)
        ax.set_ylim(-0.5, 3.5)
        ax.axis("off")

        plt.title(
            f"Binary File Structure - Message Layout ({msg_len} bytes)",
            fontsize=14,
            fontweight="bold",
        )

        plt.tight_layout()
        plt.savefig(output_path, dpi=150, bbox_inches="tight")
        plt.close()

    def _draw_message_layout(self, ax: Axes, result: BinaryAnalysisResult) -> None:
        """Draw message layout with colored fields.

        Args:
            ax: Matplotlib axes.
            result: Analysis result.
        """
        if not result.structure or not result.structure.fields:
            return

        message_length = result.structure.message_length or 0
        fields = result.structure.fields

        # Draw each field as a colored rectangle
        for field in fields:
            color = self.FIELD_COLORS.get(field.field_type.value.upper(), "#dfe6e9")

            # Draw rectangle for field
            rect = mpatches.Rectangle(
                (field.offset, 1),
                field.length,
                1,
                linewidth=1,
                edgecolor="black",
                facecolor=color,
            )
            ax.add_patch(rect)

            # Add field label
            field_center = field.offset + field.length / 2
            ax.text(
                field_center,
                1.5,
                field.name,
                ha="center",
                va="center",
                fontsize=8,
                fontweight="bold",
            )

            # Add field type
            ax.text(
                field_center,
                1.2,
                field.field_type.value,
                ha="center",
                va="center",
                fontsize=6,
                style="italic",
            )

            # Add offset labels
            ax.text(
                field.offset,
                0.8,
                f"{field.offset}",
                ha="center",
                va="top",
                fontsize=6,
                color="gray",
            )

        # Add end offset
        ax.text(
            message_length,
            0.8,
            f"{message_length}",
            ha="center",
            va="top",
            fontsize=6,
            color="gray",
        )

        # Add legend
        legend_elements = [
            mpatches.Patch(facecolor=color, edgecolor="black", label=field_type)
            for field_type, color in self.FIELD_COLORS.items()
        ]
        ax.legend(
            handles=legend_elements,
            loc="upper center",
            bbox_to_anchor=(0.5, -0.05),
            ncol=4,
            frameon=False,
        )

        # Add byte scale
        ax.text(
            -0.3,
            1.5,
            "Bytes:",
            ha="right",
            va="center",
            fontsize=8,
            fontweight="bold",
        )
