from __future__ import annotations

from dataclasses import asdict, dataclass


@dataclass(frozen=True)
class Mavic3MSpecification:
    """Current first-party specification values used by the simulation.

    The values are a research configuration receipt, not a substitute for the
    current aircraft manual, firmware release notes, or an operator's checks.
    """

    rgb_width_px: int = 5280
    rgb_height_px: int = 3956
    rgb_effective_mp: float = 20.0
    rgb_sensor: str = "4/3 CMOS"
    rgb_mechanical_shutter: bool = True
    multispectral_width_px: int = 2592
    multispectral_height_px: int = 1944
    multispectral_effective_mp_each: float = 5.0
    multispectral_camera_count: int = 4
    band_green_nm: str = "560±16"
    band_red_nm: str = "650±16"
    band_red_edge_nm: str = "730±16"
    band_nir_nm: str = "860±26"
    minimum_multispectral_interval_s: float = 2.0
    nominal_max_flight_time_min: float = 43.0
    nominal_max_hover_time_min: float = 37.0
    maximum_normal_speed_m_s: float = 15.0
    maximum_wind_resistance_m_s: float = 12.0
    net_weight_g: int = 951
    maximum_takeoff_weight_g: int = 1050
    integrated_rtk: bool = True
    integrated_sunlight_sensor: bool = True

    def as_receipt(self) -> dict[str, object]:
        return {
            "schemaVersion": 1,
            "manufacturer": "DJI",
            "model": "Mavic 3 Multispectral (Mavic 3M)",
            "accessedOn": "2026-08-04",
            "values": asdict(self),
            "primarySource": "https://enterprise.dji.com/mavic-3-m/specs",
            "manualSource": "https://enterprise.dji.com/mavic-3-m/downloads",
            "conflictResolution": (
                "The current specifications page and User Manual v1.8 are used for 5 MP and 560±16 nm. "
                "A stale FAQ passage still states 5.5 MP and 550±16 nm; the discrepancy is disclosed."
            ),
            "flightClaimBoundary": (
                "Manufacturer test-condition endurance, speed, wind, and area claims are not treated as "
                "achieved field performance or safe operating limits."
            ),
        }


MAVIC_3M = Mavic3MSpecification()


def mapping_geometry(
    altitude_agl_m: float,
    front_overlap_pct: float,
    side_overlap_pct: float,
    spec: Mavic3MSpecification = MAVIC_3M,
) -> dict[str, float]:
    if altitude_agl_m <= 0:
        raise ValueError("altitude_agl_m must be positive")
    if not 0 < front_overlap_pct < 100 or not 0 < side_overlap_pct < 100:
        raise ValueError("overlap percentages must be between zero and one hundred")
    # DJI FAQ formulas: RGB H/37.2 and multispectral H/21.7 cm/pixel.
    rgb_gsd_cm = altitude_agl_m / 37.2
    ms_gsd_cm = altitude_agl_m / 21.7
    ms_gsd_m = ms_gsd_cm / 100.0
    footprint_cross = ms_gsd_m * spec.multispectral_width_px
    footprint_along = ms_gsd_m * spec.multispectral_height_px
    return {
        "altitudeAglM": round(altitude_agl_m, 6),
        "rgbGsdCmPerPixel": round(rgb_gsd_cm, 6),
        "multispectralGsdCmPerPixel": round(ms_gsd_cm, 6),
        "multispectralFootprintCrossTrackM": round(footprint_cross, 6),
        "multispectralFootprintAlongTrackM": round(footprint_along, 6),
        "targetExposureSpacingM": round(footprint_along * (1 - front_overlap_pct / 100), 6),
        "targetLineSpacingM": round(footprint_cross * (1 - side_overlap_pct / 100), 6),
        "formulaSource": "DJI Mavic 3M FAQ; H is relative flight height in metres",
    }
