"""
Geometry Visualizer Tool

Generates SVG cross-section diagrams of fissile material configurations
for criticality safety analysis visualization.
"""

import logging
import math
from dataclasses import dataclass, field
from typing import Any

from sage.tools.scale_interface import GeometryType, ReflectionType

logger = logging.getLogger(__name__)


@dataclass
class VisualizationResult:
    """Result of geometry visualization."""

    svg_content: str
    geometry_type: str
    dimensions: dict[str, float]
    warnings: list[str] = field(default_factory=list)
    metadata: dict[str, Any] = field(default_factory=dict)


class GeometryVisualizer:
    """
    Generates SVG cross-section diagrams of fissile material configurations.

    Supports visualization of common NCS geometry types (sphere, cylinder,
    cuboid, hemisphere) with optional reflection layers.

    Example:
        visualizer = GeometryVisualizer()

        result = visualizer.visualize(
            geometry_type="sphere",
            dimensions={"radius": 5.0},
            reflection="water"
        )

        # Save SVG to file
        with open("geometry.svg", "w") as f:
            f.write(result.svg_content)
    """

    # SVG styling constants
    FISSILE_COLOR = "#ff6b6b"  # Red for fissile material
    REFLECTOR_COLORS = {
        "bare": "none",
        "water": "#6bb3ff",  # Blue
        "concrete": "#a0a0a0",  # Gray
        "steel": "#707070",  # Dark gray
    }
    REFLECTOR_THICKNESS = 15  # Default reflector thickness in SVG units

    # Default SVG dimensions
    SVG_WIDTH = 400
    SVG_HEIGHT = 400
    SVG_PADDING = 50

    def __init__(self) -> None:
        """Initialize the geometry visualizer."""
        logger.info("Initialized Geometry Visualizer")

    def visualize(
        self,
        geometry_type: str,
        dimensions: dict[str, float],
        reflection: str = "bare",
    ) -> VisualizationResult:
        """
        Generate an SVG visualization of a geometry configuration.

        Args:
            geometry_type: Type of geometry (sphere, cylinder, cuboid, hemisphere)
            dimensions: Geometry dimensions (varies by type)
            reflection: Reflection type (bare, water, concrete, steel)

        Returns:
            VisualizationResult with SVG content and metadata
        """
        warnings: list[str] = []
        metadata: dict[str, Any] = {}

        # Validate inputs
        validated_dims, dim_warnings = self._validate_dimensions(
            geometry_type, dimensions
        )
        warnings.extend(dim_warnings)

        # Validate geometry type
        try:
            geom_type = GeometryType(geometry_type.lower())
        except ValueError:
            warnings.append(f"Unknown geometry type: {geometry_type}, defaulting to sphere")
            geom_type = GeometryType.SPHERE

        # Validate reflection type
        try:
            reflect_type = ReflectionType(reflection.lower())
        except ValueError:
            warnings.append(f"Unknown reflection type: {reflection}, defaulting to bare")
            reflect_type = ReflectionType.BARE

        # Generate SVG based on geometry type
        if geom_type == GeometryType.SPHERE:
            svg_content = self._generate_sphere_svg(validated_dims, reflect_type)
        elif geom_type == GeometryType.CYLINDER:
            svg_content = self._generate_cylinder_svg(validated_dims, reflect_type)
        elif geom_type == GeometryType.CUBOID:
            svg_content = self._generate_cuboid_svg(validated_dims, reflect_type)
        elif geom_type == GeometryType.HEMISPHERE:
            svg_content = self._generate_hemisphere_svg(validated_dims, reflect_type)
        else:
            svg_content = self._generate_sphere_svg(validated_dims, reflect_type)

        # Add metadata
        metadata["reflection_type"] = reflect_type.value
        metadata["validated_dimensions"] = validated_dims

        return VisualizationResult(
            svg_content=svg_content,
            geometry_type=geom_type.value,
            dimensions=validated_dims,
            warnings=warnings,
            metadata=metadata,
        )

    def _validate_dimensions(
        self, geometry_type: str, dimensions: dict[str, float]
    ) -> tuple[dict[str, float], list[str]]:
        """
        Validate and normalize dimensions for a geometry type.

        Args:
            geometry_type: Type of geometry
            dimensions: Provided dimensions

        Returns:
            Tuple of (validated dimensions, warnings)
        """
        warnings: list[str] = []
        validated: dict[str, float] = {}

        geom_lower = geometry_type.lower()

        # Check for invalid (zero or negative) dimensions
        for key, value in dimensions.items():
            if value <= 0:
                warnings.append(f"Invalid dimension {key}={value}, using default 1.0")
                validated[key] = 1.0
            else:
                validated[key] = value

        # Set defaults based on geometry type
        if geom_lower in ("sphere", "hemisphere"):
            if "radius" not in validated:
                validated["radius"] = 5.0
                warnings.append("No radius provided, using default 5.0 cm")

        elif geom_lower == "cylinder":
            if "radius" not in validated:
                validated["radius"] = 5.0
                warnings.append("No radius provided, using default 5.0 cm")
            if "height" not in validated:
                validated["height"] = 10.0
                warnings.append("No height provided, using default 10.0 cm")

        elif geom_lower == "cuboid":
            if "x" not in validated:
                validated["x"] = 10.0
                warnings.append("No x dimension provided, using default 10.0 cm")
            if "y" not in validated:
                validated["y"] = 10.0
                warnings.append("No y dimension provided, using default 10.0 cm")
            if "z" not in validated:
                validated["z"] = 10.0
                warnings.append("No z dimension provided, using default 10.0 cm")

        return validated, warnings

    def _generate_sphere_svg(
        self, dimensions: dict[str, float], reflection: ReflectionType
    ) -> str:
        """Generate SVG for sphere cross-section."""
        radius = dimensions.get("radius", 5.0)

        # Calculate SVG coordinates
        center_x = self.SVG_WIDTH / 2
        center_y = self.SVG_HEIGHT / 2

        # Scale factor to fit in SVG
        max_radius = min(self.SVG_WIDTH, self.SVG_HEIGHT) / 2 - self.SVG_PADDING
        scale = max_radius / (radius + (self.REFLECTOR_THICKNESS if reflection != ReflectionType.BARE else 0) / 10)
        svg_radius = radius * scale

        svg_parts = [self._svg_header()]

        # Add title and description
        svg_parts.append(f'  <title>Sphere Cross-Section (r={radius:.1f} cm)</title>')

        # Draw reflector if not bare
        if reflection != ReflectionType.BARE:
            reflector_radius = svg_radius + self.REFLECTOR_THICKNESS
            reflector_color = self.REFLECTOR_COLORS.get(reflection.value, "none")
            svg_parts.append(
                f'  <circle cx="{center_x}" cy="{center_y}" r="{reflector_radius:.1f}" '
                f'fill="{reflector_color}" stroke="#333" stroke-width="1" opacity="0.6"/>'
            )

        # Draw fissile material
        svg_parts.append(
            f'  <circle cx="{center_x}" cy="{center_y}" r="{svg_radius:.1f}" '
            f'fill="{self.FISSILE_COLOR}" stroke="#333" stroke-width="2"/>'
        )

        # Add dimension lines and labels
        svg_parts.extend(self._add_dimension_label(
            center_x, center_y, center_x + svg_radius, center_y,
            f"r = {radius:.1f} cm"
        ))

        # Add reflection label if present
        if reflection != ReflectionType.BARE:
            svg_parts.append(
                f'  <text x="{center_x}" y="{center_y + svg_radius + self.REFLECTOR_THICKNESS + 20}" '
                f'text-anchor="middle" font-size="12">{reflection.value} reflection</text>'
            )

        svg_parts.append(self._svg_footer())
        return "\n".join(svg_parts)

    def _generate_cylinder_svg(
        self, dimensions: dict[str, float], reflection: ReflectionType
    ) -> str:
        """Generate SVG for cylinder cross-section (side view)."""
        radius = dimensions.get("radius", 5.0)
        height = dimensions.get("height", 10.0)

        center_x = self.SVG_WIDTH / 2
        center_y = self.SVG_HEIGHT / 2

        # Scale to fit
        max_dim = max(radius * 2, height)
        available = min(self.SVG_WIDTH, self.SVG_HEIGHT) - 2 * self.SVG_PADDING
        scale = available / (max_dim + (self.REFLECTOR_THICKNESS * 2 if reflection != ReflectionType.BARE else 0) / 10)

        svg_width = radius * 2 * scale
        svg_height = height * scale

        svg_parts = [self._svg_header()]
        svg_parts.append(f'  <title>Cylinder Cross-Section (r={radius:.1f} cm, h={height:.1f} cm)</title>')

        rect_x = center_x - svg_width / 2
        rect_y = center_y - svg_height / 2

        # Draw reflector if not bare
        if reflection != ReflectionType.BARE:
            reflector_color = self.REFLECTOR_COLORS.get(reflection.value, "none")
            svg_parts.append(
                f'  <rect x="{rect_x - self.REFLECTOR_THICKNESS}" '
                f'y="{rect_y - self.REFLECTOR_THICKNESS}" '
                f'width="{svg_width + 2 * self.REFLECTOR_THICKNESS}" '
                f'height="{svg_height + 2 * self.REFLECTOR_THICKNESS}" '
                f'fill="{reflector_color}" stroke="#333" stroke-width="1" opacity="0.6" rx="5"/>'
            )

        # Draw fissile material (rectangle for side view)
        svg_parts.append(
            f'  <rect x="{rect_x}" y="{rect_y}" '
            f'width="{svg_width}" height="{svg_height}" '
            f'fill="{self.FISSILE_COLOR}" stroke="#333" stroke-width="2" rx="3"/>'
        )

        # Add dimension labels
        svg_parts.extend(self._add_dimension_label(
            rect_x, rect_y + svg_height + 20,
            rect_x + svg_width, rect_y + svg_height + 20,
            f"d = {radius * 2:.1f} cm"
        ))
        svg_parts.extend(self._add_dimension_label(
            rect_x + svg_width + 20, rect_y,
            rect_x + svg_width + 20, rect_y + svg_height,
            f"h = {height:.1f} cm"
        ))

        if reflection != ReflectionType.BARE:
            svg_parts.append(
                f'  <text x="{center_x}" y="{rect_y + svg_height + self.REFLECTOR_THICKNESS + 45}" '
                f'text-anchor="middle" font-size="12">{reflection.value} reflection</text>'
            )

        svg_parts.append(self._svg_footer())
        return "\n".join(svg_parts)

    def _generate_cuboid_svg(
        self, dimensions: dict[str, float], reflection: ReflectionType
    ) -> str:
        """Generate SVG for cuboid cross-section (top view showing x and y)."""
        x_dim = dimensions.get("x", 10.0)
        y_dim = dimensions.get("y", 10.0)
        z_dim = dimensions.get("z", 10.0)

        center_x = self.SVG_WIDTH / 2
        center_y = self.SVG_HEIGHT / 2

        # Scale to fit
        max_dim = max(x_dim, y_dim)
        available = min(self.SVG_WIDTH, self.SVG_HEIGHT) - 2 * self.SVG_PADDING
        scale = available / (max_dim + (self.REFLECTOR_THICKNESS * 2 if reflection != ReflectionType.BARE else 0) / 10)

        svg_width = x_dim * scale
        svg_height = y_dim * scale

        svg_parts = [self._svg_header()]
        svg_parts.append(f'  <title>Cuboid Cross-Section (x={x_dim:.1f}, y={y_dim:.1f}, z={z_dim:.1f} cm)</title>')

        rect_x = center_x - svg_width / 2
        rect_y = center_y - svg_height / 2

        # Draw reflector if not bare
        if reflection != ReflectionType.BARE:
            reflector_color = self.REFLECTOR_COLORS.get(reflection.value, "none")
            svg_parts.append(
                f'  <rect x="{rect_x - self.REFLECTOR_THICKNESS}" '
                f'y="{rect_y - self.REFLECTOR_THICKNESS}" '
                f'width="{svg_width + 2 * self.REFLECTOR_THICKNESS}" '
                f'height="{svg_height + 2 * self.REFLECTOR_THICKNESS}" '
                f'fill="{reflector_color}" stroke="#333" stroke-width="1" opacity="0.6"/>'
            )

        # Draw fissile material
        svg_parts.append(
            f'  <rect x="{rect_x}" y="{rect_y}" '
            f'width="{svg_width}" height="{svg_height}" '
            f'fill="{self.FISSILE_COLOR}" stroke="#333" stroke-width="2"/>'
        )

        # Add dimension labels
        svg_parts.extend(self._add_dimension_label(
            rect_x, rect_y + svg_height + 20,
            rect_x + svg_width, rect_y + svg_height + 20,
            f"x = {x_dim:.1f} cm"
        ))
        svg_parts.extend(self._add_dimension_label(
            rect_x + svg_width + 20, rect_y,
            rect_x + svg_width + 20, rect_y + svg_height,
            f"y = {y_dim:.1f} cm"
        ))

        # Add z dimension as text note
        svg_parts.append(
            f'  <text x="{center_x}" y="{rect_y - 10}" '
            f'text-anchor="middle" font-size="11" fill="#666">(z = {z_dim:.1f} cm, not shown)</text>'
        )

        if reflection != ReflectionType.BARE:
            svg_parts.append(
                f'  <text x="{center_x}" y="{rect_y + svg_height + self.REFLECTOR_THICKNESS + 45}" '
                f'text-anchor="middle" font-size="12">{reflection.value} reflection</text>'
            )

        svg_parts.append(self._svg_footer())
        return "\n".join(svg_parts)

    def _generate_hemisphere_svg(
        self, dimensions: dict[str, float], reflection: ReflectionType
    ) -> str:
        """Generate SVG for hemisphere cross-section."""
        radius = dimensions.get("radius", 5.0)

        center_x = self.SVG_WIDTH / 2
        center_y = self.SVG_HEIGHT / 2 + 30  # Shift down to center visually

        # Scale factor to fit
        max_radius = min(self.SVG_WIDTH, self.SVG_HEIGHT) / 2 - self.SVG_PADDING
        scale = max_radius / (radius + (self.REFLECTOR_THICKNESS if reflection != ReflectionType.BARE else 0) / 10)
        svg_radius = radius * scale

        svg_parts = [self._svg_header()]
        svg_parts.append(f'  <title>Hemisphere Cross-Section (r={radius:.1f} cm)</title>')

        # Draw reflector arc if not bare
        if reflection != ReflectionType.BARE:
            reflector_radius = svg_radius + self.REFLECTOR_THICKNESS
            reflector_color = self.REFLECTOR_COLORS.get(reflection.value, "none")
            # Hemisphere path (arc + line)
            svg_parts.append(
                f'  <path d="M {center_x - reflector_radius} {center_y} '
                f'A {reflector_radius} {reflector_radius} 0 0 1 {center_x + reflector_radius} {center_y} '
                f'L {center_x - reflector_radius} {center_y} Z" '
                f'fill="{reflector_color}" stroke="#333" stroke-width="1" opacity="0.6"/>'
            )

        # Draw fissile hemisphere (arc + line)
        svg_parts.append(
            f'  <path d="M {center_x - svg_radius} {center_y} '
            f'A {svg_radius} {svg_radius} 0 0 1 {center_x + svg_radius} {center_y} '
            f'L {center_x - svg_radius} {center_y} Z" '
            f'fill="{self.FISSILE_COLOR}" stroke="#333" stroke-width="2"/>'
        )

        # Add dimension line
        svg_parts.extend(self._add_dimension_label(
            center_x, center_y, center_x + svg_radius, center_y,
            f"r = {radius:.1f} cm"
        ))

        if reflection != ReflectionType.BARE:
            svg_parts.append(
                f'  <text x="{center_x}" y="{center_y + 40}" '
                f'text-anchor="middle" font-size="12">{reflection.value} reflection</text>'
            )

        svg_parts.append(self._svg_footer())
        return "\n".join(svg_parts)

    def _svg_header(self) -> str:
        """Generate SVG header."""
        return (
            f'<svg xmlns="http://www.w3.org/2000/svg" '
            f'width="{self.SVG_WIDTH}" height="{self.SVG_HEIGHT}" '
            f'viewBox="0 0 {self.SVG_WIDTH} {self.SVG_HEIGHT}">\n'
            f'  <rect width="100%" height="100%" fill="#f8f9fa"/>\n'
            f'  <style>\n'
            f'    text {{ font-family: Arial, sans-serif; }}\n'
            f'  </style>'
        )

    def _svg_footer(self) -> str:
        """Generate SVG footer."""
        return '</svg>'

    def _add_dimension_label(
        self, x1: float, y1: float, x2: float, y2: float, label: str
    ) -> list[str]:
        """
        Add a dimension line with label.

        Args:
            x1, y1: Start point
            x2, y2: End point
            label: Dimension label text

        Returns:
            List of SVG elements
        """
        mid_x = (x1 + x2) / 2
        mid_y = (y1 + y2) / 2

        elements = [
            # Dimension line
            f'  <line x1="{x1}" y1="{y1}" x2="{x2}" y2="{y2}" '
            f'stroke="#333" stroke-width="1" marker-start="url(#arrow)" marker-end="url(#arrow)"/>',
            # Label
            f'  <text x="{mid_x}" y="{mid_y - 5}" text-anchor="middle" font-size="11">{label}</text>',
        ]

        # Add arrow marker definition if this is the first dimension line
        if not hasattr(self, '_arrow_defined'):
            elements.insert(0, '''  <defs>
    <marker id="arrow" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto">
      <path d="M 0 0 L 6 3 L 0 6 z" fill="#333"/>
    </marker>
  </defs>''')
            self._arrow_defined = True

        return elements
