//! Northdata `suggest.json` — mirrors `fetch_northdata_suggest` / `northdata_headers` from
//! `scripts/scrape_global_registry.py`.

use std::time::Duration;

use percent_encoding::{utf8_percent_encode, NON_ALPHANUMERIC};
use serde_json::{Map, Value};
use tokio::time::sleep;

/// Same default UA as Python `NORTHDATA_DEFAULT_UA`.
pub const DEFAULT_UA: &str = concat!(
    "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 ",
    "(KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36"
);

const MAX_RETRIES: u32 = 4;

/// GET suggest.json; returns raw `results` entries that are JSON objects.
pub async fn fetch_northdata_suggest(
    client: &reqwest::Client,
    base_url: &str,
    query: &str,
    countries: &str,
    cookie: Option<&str>,
) -> Vec<Map<String, Value>> {
    let root = base_url.trim_end_matches('/');
    let q = utf8_percent_encode(query, NON_ALPHANUMERIC);
    let cc_enc = utf8_percent_encode(countries, NON_ALPHANUMERIC);
    let url = format!("{root}/suggest.json?query={q}&countries={cc_enc}");

    let mut delay_secs: f64 = 1.0;
    let mut last_err = String::new();

    for attempt in 0..MAX_RETRIES {
        let mut req = client
            .get(&url)
            .header("accept", "application/json, text/javascript, */*; q=0.01")
            .header("accept-language", "en-US,en;q=0.9")
            .header("referer", "https://www.northdata.com/")
            .header("user-agent", DEFAULT_UA)
            .header("x-requested-with", "XMLHttpRequest");
        if let Some(c) = cookie.filter(|s| !s.is_empty()) {
            req = req.header("cookie", c);
        }

        match req.send().await {
            Ok(resp) => {
                let status = resp.status();
                if status.is_success() {
                    let Ok(val) = resp.json::<Value>().await else {
                        tracing::warn!("Northdata: invalid JSON for {}", query);
                        return vec![];
                    };
                    let results = val.get("results").and_then(|r| r.as_array());
                    let Some(arr) = results else {
                        return vec![];
                    };
                    return arr.iter().filter_map(|v| v.as_object().cloned()).collect();
                }
                if status.as_u16() == 429 || status.as_u16() == 503 {
                    last_err = format!("HTTP {}", status);
                    tracing::warn!("Northdata {}: retry in {:.0}s", status, delay_secs);
                    sleep(Duration::from_secs_f64(delay_secs)).await;
                    delay_secs = (delay_secs * 2.0).min(30.0);
                    continue;
                }
                let text = resp.text().await.unwrap_or_default();
                tracing::warn!(
                    "Northdata {}: {}",
                    status,
                    text.chars().take(200).collect::<String>()
                );
                return vec![];
            }
            Err(e) => {
                last_err = e.to_string();
                tracing::warn!("Northdata request error (attempt {}): {}", attempt + 1, e);
                sleep(Duration::from_secs_f64(delay_secs)).await;
                delay_secs = (delay_secs * 2.0).min(30.0);
            }
        }
    }

    tracing::error!("Northdata suggest failed after retries: {last_err}");
    vec![]
}
