from __future__ import annotations

from collections.abc import Iterable
from typing import Any

from .mission import MissionParameters, VirtualField, plan_virtual_mission
from .specs import MAVIC_3M, mapping_geometry


def trigger_limited_max_speed_m_s(
    altitude_agl_m: float,
    front_overlap_pct: float,
    capture_interval_s: float = MAVIC_3M.minimum_multispectral_interval_s,
) -> float:
    """Return the fastest ground speed that preserves overlap and capture cadence.

    The result is a geometry/cadence constraint, not a safe flight speed. Wind,
    terrain, light, regulatory limits, aircraft state, and the operator can all
    require a lower speed or cancellation.
    """

    if capture_interval_s <= 0:
        raise ValueError("capture_interval_s must be positive")
    geometry = mapping_geometry(altitude_agl_m, front_overlap_pct, 70.0)
    return float(geometry["targetExposureSpacingM"]) / capture_interval_s


def minimum_altitude_for_speed_m(
    requested_speed_m_s: float,
    front_overlap_pct: float,
    capture_interval_s: float = MAVIC_3M.minimum_multispectral_interval_s,
) -> float:
    """Solve the Mavic 3M FAQ GSD model for the minimum cadence-feasible AGL."""

    if requested_speed_m_s <= 0:
        raise ValueError("requested_speed_m_s must be positive")
    if not 0 < front_overlap_pct < 100:
        raise ValueError("front_overlap_pct must be between zero and one hundred")
    if capture_interval_s <= 0:
        raise ValueError("capture_interval_s must be positive")
    # footprint_along = H / 21.7 cm/px / 100 * 1944 px
    footprint_per_altitude = MAVIC_3M.multispectral_height_px / 2170.0
    available_spacing_fraction = 1.0 - front_overlap_pct / 100.0
    return requested_speed_m_s * capture_interval_s / (
        footprint_per_altitude * available_spacing_fraction
    )


def mission_envelope(
    altitudes_m: Iterable[float] = (30, 45, 60, 75, 90, 105, 120),
    front_overlaps_pct: Iterable[float] = (70, 75, 80, 85, 90),
    requested_speed_m_s: float = 5.0,
    side_overlap_pct: float = 70.0,
    field: VirtualField = VirtualField(),
) -> list[dict[str, Any]]:
    """Evaluate a transparent altitude/overlap grid for the virtual field."""

    rows: list[dict[str, Any]] = []
    for altitude in altitudes_m:
        for front_overlap in front_overlaps_pct:
            parameters = MissionParameters(
                altitude_agl_m=float(altitude),
                requested_speed_m_s=requested_speed_m_s,
                front_overlap_pct=float(front_overlap),
                side_overlap_pct=side_overlap_pct,
            )
            mission = plan_virtual_mission(field, parameters)
            cadence_limit = trigger_limited_max_speed_m_s(
                float(altitude), float(front_overlap), parameters.capture_interval_s
            )
            rows.append({
                "altitudeAglM": float(altitude),
                "frontOverlapPct": float(front_overlap),
                "sideOverlapPct": side_overlap_pct,
                "requestedSpeedMS": requested_speed_m_s,
                "triggerLimitedMaxSpeedMS": round(cadence_limit, 6),
                "actualPlannedSpeedMS": mission["actualSpeedMS"],
                "speedClampedByCadence": mission["actualSpeedMS"] + 1e-9 < requested_speed_m_s,
                "multispectralGsdCmPerPixel": mission["cameraGeometry"]["multispectralGsdCmPerPixel"],
                "scanLineCount": mission["scanLineCount"],
                "captureEventCount": mission["captureEventCount"],
                "expectedImageFileCount": mission["expectedImageFileCount"],
                "estimatedMissionMinutes": mission["estimatedMissionMinutes"],
                "planningReserveMinutes": mission["planningReserveMinutes"],
                "singleFlightPlanningGate": mission["singleFlightPlanningGate"],
                "claimBoundary": (
                    "Synthetic geometry and timing only; not a safe-flight, endurance, "
                    "image-quality, legal, or agronomic guarantee."
                ),
            })
    return rows

