"""
Group size computation from PointOfImpact real-world coordinates.

All measurements in millimetres. Origin is point-of-aim:
  x > 0 = right (windage),  y > 0 = up (elevation).
"""
import math


def compute_group_size(
    points: list[tuple[float, float]],
    distance_m: float | None = None,
) -> dict:
    """
    Compute ballistic group metrics from a list of (x_mm, y_mm) coordinates.

    Args:
        points:     list of (x_mm, y_mm) tuples — minimum 2 required.
        distance_m: shooting distance in metres, used for MOA conversion.
                    Pass None to leave MOA fields as None.

    Returns:
        dict with keys matching GroupPhotoAnalysis fields.
    """
    if len(points) < 2:
        raise ValueError("At least 2 points of impact are required.")

    xs = [p[0] for p in points]
    ys = [p[1] for p in points]
    n = len(points)

    # Extreme spread: maximum pairwise distance
    group_size_mm = 0.0
    for i in range(n):
        for j in range(i + 1, n):
            d = math.sqrt((xs[i] - xs[j]) ** 2 + (ys[i] - ys[j]) ** 2)
            if d > group_size_mm:
                group_size_mm = d

    # Centroid
    cx = sum(xs) / n
    cy = sum(ys) / n

    # Mean radius: average distance from centroid
    mean_radius_mm = sum(
        math.sqrt((x - cx) ** 2 + (y - cy) ** 2) for x, y in points
    ) / n

    def to_moa(mm: float) -> float | None:
        """Convert mm at distance_m to MOA. 1 MOA ≈ 0.29089 mm/m at that distance."""
        if distance_m is None or distance_m <= 0:
            return None
        return round(mm / (distance_m * 0.29089), 3)

    return {
        'group_size_mm':        round(group_size_mm, 2),
        'group_size_moa':       to_moa(group_size_mm),
        'mean_radius_mm':       round(mean_radius_mm, 2),
        'mean_radius_moa':      to_moa(mean_radius_mm),
        'windage_offset_mm':    round(cx, 2),
        'windage_offset_moa':   to_moa(cx),
        'elevation_offset_mm':  round(cy, 2),
        'elevation_offset_moa': to_moa(cy),
    }
