//! The v0.1 acceptance bar, end to end through the public API: given a
//! US airport, a route string, a point, a radius, or a polygon — under a
//! SPECIFIED NASR cycle — resolve entities, expand the route, generate a
//! corridor, and spatially associate geo-referenced products.
#![allow(clippy::expect_used, clippy::panic, missing_docs)]

use aerocontext_core::{
    Airway, AirwayLocation, AirwayPoint, Area, GeoPoint, NavDataCycle, NavDataSnapshot, NavPoint,
    NavPointKind,
};
use aerocontext_planning::corridor::Corridor;
use aerocontext_planning::route;
use chrono::NaiveDate;

fn specified_cycle() -> NavDataCycle {
    // The cycle is pinned explicitly — selection by date is the caller's
    // job (Manifest::entry_for); everything below is cycle-deterministic.
    NavDataCycle::faa_nasr(NaiveDate::from_ymd_opt(2026, 6, 11).expect("date")).expect("cycle")
}

fn snapshot() -> NavDataSnapshot {
    let points = vec![
        NavPoint::new(
            "KSFO",
            NavPointKind::Airport,
            GeoPoint {
                lat: 37.62,
                lon: -122.37,
            },
        )
        .with_name(Some("SAN FRANCISCO INTL".to_owned())),
        NavPoint::new(
            "KSLC",
            NavPointKind::Airport,
            GeoPoint {
                lat: 40.79,
                lon: -111.98,
            },
        ),
        NavPoint::new(
            "ORRCA",
            NavPointKind::Waypoint,
            GeoPoint {
                lat: 38.2,
                lon: -121.0,
            },
        ),
        NavPoint::new(
            "MOGEE",
            NavPointKind::Waypoint,
            GeoPoint {
                lat: 39.0,
                lon: -118.5,
            },
        ),
        NavPoint::new(
            "GALLI",
            NavPointKind::Waypoint,
            GeoPoint {
                lat: 39.9,
                lon: -114.8,
            },
        ),
    ];
    NavDataSnapshot::new(specified_cycle(), points).with_airways(vec![Airway::new(
        "Q120",
        AirwayLocation::Conus,
        vec![
            AirwayPoint::new("ORRCA"),
            AirwayPoint::new("MOGEE"),
            AirwayPoint::new("GALLI"),
        ],
    )])
}

#[test]
fn airport_route_corridor_and_spatial_association_under_one_cycle() {
    let snapshot = snapshot();
    assert!(snapshot.contains(NaiveDate::from_ymd_opt(2026, 6, 20).expect("date")));

    // 1. Airport resolution.
    let ksfo = snapshot.resolve("KSFO").expect("airport resolves");
    assert_eq!(ksfo.name.as_deref(), Some("SAN FRANCISCO INTL"));

    // 2. Route-string expansion: SID token, airway traversal, DCT.
    let route =
        route::expand_str("TRUKN2 ORRCA Q120 GALLI DCT KSLC", &snapshot).expect("route expands");
    let idents: Vec<&str> = route
        .points
        .iter()
        .filter_map(|p| p.ident.as_deref())
        .collect();
    assert_eq!(idents, vec!["ORRCA", "MOGEE", "GALLI", "KSLC"]);
    assert_eq!(route.procedures.len(), 1, "SID recognized, not expanded");

    // 3. Corridor generation (25 NM half-width).
    let corridor = Corridor::around_route(&route, 25.0).expect("corridor builds");

    // 4. Spatial association hooks.
    //    A PIREP-like product on the airway is inside; a distant one is
    //    not (leg-wise predicates)…
    let on_route = GeoPoint {
        lat: 38.6,
        lon: -119.8,
    };
    let far_away = GeoPoint {
        lat: 33.9,
        lon: -118.4,
    };
    assert!(corridor.contains(on_route));
    assert!(!corridor.contains(far_away));
    assert!(corridor.distance_to_centerline_nm(on_route) < 25.0);

    //    …and the corridor hands off to core Area for provider bboxes and
    //    TFR/NOTAM-style polygon filtering.
    let area = corridor.area();
    let (sw, ne) = area.enclosing_bbox().expect("bbox for providers");
    assert!(sw.lat < 38.0 && ne.lat > 40.0, "bbox spans the route");
    assert_eq!(area.contains(on_route), Some(true));

    // 5. Point/radius and polygon areas answer the same spatial question.
    let around_ksfo = Area::PointRadius {
        center: ksfo.position,
        radius_nm: 30.0,
    };
    assert_eq!(
        area.contains(ksfo.position),
        Some(false),
        "corridor starts at ORRCA"
    );
    assert_eq!(around_ksfo.contains(ksfo.position), Some(true));
}
