//! The `resolve` tool: identifier → published navigation point.

use aerocontext_core::{NavDataSnapshot, NavPointKind};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};

use crate::error::ToolError;

/// Parameters for the `resolve` tool.
#[derive(Debug, Deserialize, JsonSchema)]
pub struct ResolveParams {
    /// Identifier to resolve: an ICAO/FAA airport (`KSFO`, `SFO`),
    /// navaid (`SAC` — the Sacramento VOR), or published fix (`ARCHI`).
    /// Case-insensitive. When an airport shares the ident with a navaid
    /// or fix, the airport wins (`OAK` is Oakland Intl, not the OAK
    /// VOR).
    pub ident: String,
}

/// What the `resolve` tool returns.
#[derive(Debug, Serialize, JsonSchema)]
pub struct ResolveReport {
    /// Whether the identifier exists in the current snapshot.
    pub found: bool,
    /// The identifier as published (or as queried, when not found).
    pub ident: String,
    /// Point family: `airport`, `waypoint`, `navaid`, or a source-named
    /// kind. `null` when not found.
    pub kind: Option<String>,
    /// WGS84 latitude in degrees, north positive. `null` when not found.
    pub lat: Option<f64>,
    /// WGS84 longitude in degrees, east positive. `null` when not found.
    pub lon: Option<f64>,
    /// Published display name, when the source carries one.
    pub name: Option<String>,
    /// Effective range of the snapshot consulted, `YYYY-MM-DD..YYYY-MM-DD`.
    pub cycle: String,
    /// Present only when not found: what to try next.
    pub note: Option<String>,
}

/// Resolve `ident` in `snapshot`, shaping the wire report.
pub fn resolve_in_snapshot(
    snapshot: &NavDataSnapshot,
    ident: &str,
) -> Result<ResolveReport, ToolError> {
    let wanted = ident.trim();
    if wanted.is_empty() {
        return Err(ToolError::InvalidParam {
            message: "ident must be a non-empty identifier, e.g. \"KSFO\"".to_owned(),
        });
    }
    let cycle = format!(
        "{}..{}",
        snapshot.cycle.effective_on, snapshot.cycle.next_effective_on
    );
    Ok(match snapshot.resolve(wanted) {
        Some(point) => ResolveReport {
            found: true,
            ident: point.ident.clone(),
            kind: Some(kind_label(&point.kind)),
            lat: Some(point.position.lat),
            lon: Some(point.position.lon),
            name: point.name.clone(),
            cycle,
            note: None,
        },
        None => ResolveReport {
            found: false,
            ident: wanted.to_owned(),
            kind: None,
            lat: None,
            lon: None,
            name: None,
            cycle: cycle.clone(),
            note: Some(format!(
                "{wanted} is not in the FAA NASR snapshot effective {cycle}; \
                 check the spelling or try the ICAO form (e.g. KSFO for SFO)"
            )),
        },
    })
}

/// Stable lowercase label for a point kind.
fn kind_label(kind: &NavPointKind) -> String {
    match kind {
        NavPointKind::Airport => "airport".to_owned(),
        NavPointKind::Waypoint => "waypoint".to_owned(),
        NavPointKind::Navaid => "navaid".to_owned(),
        NavPointKind::Other(name) => name.clone(),
        _ => "other".to_owned(),
    }
}

#[cfg(test)]
mod tests;
