// src/cli/validate.rs
use anyhow::{Result, bail};

pub const TIP_NEGATIVE_COORDS: &str =
    "Note: for negative coordinates, use the '=' form, e.g.:\n  near --r 50 --x=7100 --y=-190";

pub fn validate_near(
    unknown: bool,
    fid: &Option<i64>,
    planet: &Option<String>,
    x: &Option<f64>,
    y: &Option<f64>,
) -> Result<()> {
    if unknown {
        if fid.is_none() {
            bail!("--fid is required with --unknown");
        }

        if planet.is_some() || x.is_some() || y.is_some() {
            bail!("When using --unknown, do not specify a planet name or --x/--y coordinates.");
        }

        return Ok(());
    }

    if fid.is_some() {
        bail!("--fid can only be used with --unknown.");
    }

    if planet.is_some() {
        if x.is_some() || y.is_some() {
            bail!("Specify either a planet name or --x/--y coordinates, not both.");
        }

        return Ok(());
    }

    if x.is_some() && y.is_some() {
        return Ok(());
    }

    if x.is_some() || y.is_some() {
        bail!("You must specify both --x and --y coordinates.\n\n{TIP_NEGATIVE_COORDS}");
    }

    bail!(
        "You must specify either:\n\
         \n\
         - <PLANET_NAME>\n\
         \n\
         OR\n\
         \n\
         - --x=<VALUE> --y=<VALUE>\n\
         \n\
         OR\n\
         \n\
         - --unknown --fid <FID>\n\
         \n\
         {TIP_NEGATIVE_COORDS}"
    )
}
pub fn validate_search(filter: &crate::model::SearchFilter) -> Result<()> {
    validate_limit(filter.limit, "search")?;

    let has_query = filter
        .query
        .as_deref()
        .map(str::trim)
        .filter(|s| !s.is_empty())
        .is_some();

    let has_filter = filter.region.is_some()
        || filter.sector.is_some()
        || filter.grid.is_some()
        || filter.status.is_some()
        || filter.canon == Some(true)
        || filter.legends == Some(true);

    if !has_query && !has_filter {
        bail!(
            "search requires at least a query or one filter (--region, --sector, --grid, --status, --canon, --legends)"
        );
    }

    if let Some(st) = filter
        .status
        .as_deref()
        .map(str::trim)
        .filter(|s| !s.is_empty())
    {
        const VALID: &[&str] = &[
            "active", "inserted", "modified", "skipped", "deleted", "invalid",
        ];
        if !VALID.iter().any(|v| v.eq_ignore_ascii_case(st)) {
            bail!(
                "invalid --status value '{}'. Valid values: {}",
                st,
                VALID.join(", ")
            );
        }
    }

    Ok(())
}

pub fn validate_route_id(route_id: i64, ctx: &str) -> anyhow::Result<()> {
    if route_id <= 0 {
        anyhow::bail!("Invalid route id for {ctx}: {route_id} (must be > 0)");
    }
    Ok(())
}

pub fn validate_route_compute(from: &str, to: &str) -> anyhow::Result<()> {
    let f = from.trim();
    let t = to.trim();

    if f.is_empty() || t.is_empty() {
        anyhow::bail!("FROM and TO must be non-empty");
    }
    if f.eq_ignore_ascii_case(t) {
        anyhow::bail!("FROM and TO must be different");
    }
    Ok(())
}

pub fn validate_route_planets(planets: &[String]) -> anyhow::Result<()> {
    if planets.len() < 2 {
        anyhow::bail!("Route compute requires at least two planets.");
    }
    for (idx, planet) in planets.iter().enumerate() {
        if planet.trim().is_empty() {
            anyhow::bail!("Planet {} cannot be empty.", idx + 1);
        }
    }
    for window in planets.windows(2) {
        let from = window[0].trim();
        let to = window[1].trim();
        if from.eq_ignore_ascii_case(to) {
            anyhow::bail!("Adjacent planets must be different ({} → {}).", from, to);
        }
    }
    Ok(())
}

pub fn validate_limit(limit: i64, ctx: &str) -> anyhow::Result<()> {
    if limit <= 0 {
        anyhow::bail!("Invalid limit for {ctx}: {limit} (must be > 0)");
    }
    Ok(())
}
