from __future__ import annotations

from enum import StrEnum
from typing import Literal, Optional, Dict, Any

from pydantic import BaseModel, Field, ConfigDict


class CalibrationType(StrEnum):
    """Supported calibration types."""
    GYROSCOPE = "gyroscope"
    ACCELEROMETER = "accelerometer"
    MAGNETOMETER = "magnetometer"
    HORIZON = "horizon"
    ESC = "esc"


class CalibrationRequest(BaseModel):
    """Request model for calibration operations.

    Exactly one calibration type is selected via `calibration_type`.
    """

    model_config = ConfigDict(
        json_schema_extra={
            "example": {
                "calibration_type": "accelerometer",
                "port": "/dev/pixhawk",
                "baudrate": 2_000_000,
            }
        }
    )

    calibration_type: CalibrationType = Field(
        description=(
            "Which calibration to run. "
            "One of: gyroscope, accelerometer, magnetometer, horizon, esc."
        )
    )
    port: str = Field(
        default="/dev/pixhawk",
        description="Serial port device.",
    )
    baudrate: int = Field(
        default=2_000_000,
        description="Serial baudrate (must be positive).",
        ge=1,
    )

class CalibrationHealth(BaseModel):
    """Calibration health response."""

    gyroscope_ok: bool = Field(..., description="Whether the gyroscope is calibrated.")
    accelerometer_ok: bool = Field(..., description="Whether the accelerometer is calibrated.")
    magnetometer_ok: bool = Field(..., description="Whether the magnetometer is calibrated.")

    @property
    def requires_calibration(self) -> bool:
        """Whether the calibration is required."""
        return not (self.gyroscope_ok and self.accelerometer_ok and self.magnetometer_ok)

class Px4HealthResponse(BaseModel):
    """
    High-level PX4 health snapshot.

    Combines:
    - overall status (ok / degraded / disconnected)
    - connection information
    - calibration health (if available)
    - GPS fix/used satellites
    - armable flag
    - optional raw health flags for debugging/advanced UI
    """

    status: Literal["ok", "degraded", "disconnected"] = Field(
        ...,
        description="High-level PX4 health status derived from telemetry.",
    )
    connection_port: str = Field(
        ...,
        description="Serial port used to reach the PX4 autopilot.",
    )
    connection_baudrate: int = Field(
        ...,
        description="Baudrate used on the serial link to PX4.",
    )

    calibration: Optional[CalibrationHealth] = Field(
        default=None,
        description="Sensor calibration health, if telemetry health was received.",
    )

    gps_fix_type: Optional[str] = Field(
        default=None,
        description=(
            "GPS fix type as a lowercase string (e.g. 'no_fix', '2d_fix', '3d_fix', "
            "'rtk_float', 'rtk_fix'), if GPS info is available."
        ),
    )
    satellites_used: Optional[int] = Field(
        default=None,
        ge=0,
        description=(
            "Number of satellites currently used for the positioning solution, "
            "if GPS info is available."
        ),
    )

    armable: Optional[bool] = Field(
        default=None,
        description="True if PX4 considers the vehicle armable.",
    )

    raw_health: Optional[Dict[str, Any]] = Field(
        default=None,
        description=(
            "Optional low-level health flags directly from MAVSDK telemetry.health(), "
            "for debugging or advanced UI use."
        ),
    )