//! Published airway (ATS route) domain types.

use serde::{Deserialize, Serialize};

/// Geographic family of an airway, mirroring the NASR `AWY_LOCATION`
/// regions; the same airway ident can exist independently per location.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub enum AirwayLocation {
    /// Contiguous United States.
    Conus,
    /// Alaska.
    Alaska,
    /// Hawaii.
    Hawaii,
}

impl AirwayLocation {
    /// The NASR location code: `"C"`, `"A"`, or `"H"`.
    pub fn code(self) -> &'static str {
        match self {
            Self::Conus => "C",
            Self::Alaska => "A",
            Self::Hawaii => "H",
        }
    }
}

/// One point along an airway, in published sequence order.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct AirwayPoint {
    /// Published identifier of the fix or navaid.
    pub ident: String,
    /// ICAO region of the point (e.g. `"K2"`), when the source provides
    /// one — used to disambiguate idents that recur across regions.
    pub icao_region: Option<String>,
    /// The segment from this point to the next is a published gap
    /// (discontinued or unusable); route expansion must not traverse it.
    pub gap_to_next: bool,
}

impl AirwayPoint {
    /// A point with no region and no gap.
    pub fn new(ident: impl Into<String>) -> Self {
        Self {
            ident: ident.into(),
            icao_region: None,
            gap_to_next: false,
        }
    }

    /// Set the ICAO region.
    #[must_use]
    pub fn with_icao_region(mut self, icao_region: Option<String>) -> Self {
        self.icao_region = icao_region;
        self
    }

    /// Mark the segment to the next point as a gap.
    #[must_use]
    pub fn with_gap_to_next(mut self, gap_to_next: bool) -> Self {
        self.gap_to_next = gap_to_next;
        self
    }
}

/// A published airway: an ordered list of points.
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[non_exhaustive]
pub struct Airway {
    /// Airway designator, e.g. `"V25"`, `"J94"`, `"Q120"`.
    pub ident: String,
    /// Geographic family the designator belongs to.
    pub location: AirwayLocation,
    /// Points in published sequence order.
    pub points: Vec<AirwayPoint>,
}

impl Airway {
    /// An airway over the given ordered points.
    pub fn new(
        ident: impl Into<String>,
        location: AirwayLocation,
        points: Vec<AirwayPoint>,
    ) -> Self {
        Self {
            ident: ident.into(),
            location,
            points,
        }
    }
}
