use std::collections::HashMap;
use std::sync::OnceLock;

/// whois server overrides from Lists.toml ("io:whois.nic.io" style entries)
#[derive(Debug, Clone, Default)]
pub struct WhoisOverrides {
    map: HashMap<String, String>,
}

impl WhoisOverrides {
    pub fn get_server(&self, tld: &str) -> Option<&str> {
        self.map.get(&tld.to_lowercase()).map(|s| s.as_str())
    }
}

struct NamedList {
    name: String,
    tlds: Vec<String>,
}

struct ParsedLists {
    lists: Vec<NamedList>,
    whois_overrides: WhoisOverrides,
}

// parse "tld" or "tld:whois_server"
fn parse_entry(entry: &str) -> (String, Option<String>) {
    if let Some(pos) = entry.find(':') {
        (entry[..pos].to_string(), Some(entry[pos + 1..].to_string()))
    } else {
        (entry.to_string(), None)
    }
}

// parse more 
fn parse_list(entries: &[toml::Value], overrides: &mut HashMap<String, String>) -> Vec<String> {
    entries
        .iter()
        .filter_map(|v| v.as_str())
        .map(|entry| {
            let (tld, server) = parse_entry(entry);
            if let Some(s) = server {
                overrides.insert(tld.to_lowercase(), s);
            }
            tld
        })
        .collect()
}

static PARSED_LISTS: OnceLock<ParsedLists> = OnceLock::new();

fn parsed_lists() -> &'static ParsedLists {
    PARSED_LISTS.get_or_init(|| {
        let raw: toml::Value =
            toml::from_str(include_str!("../Lists.toml")).expect("Lists.toml must be valid TOML");

        let table = raw.as_table().expect("Lists.toml must be a TOML table");

        // Build list names in the order build.rs found em
        let ordered_names: Vec<&str> = env!("HOARDOM_LIST_NAMES").split(',').collect();

        let mut overrides = HashMap::new();
        let mut lists = Vec::new();

        for name in &ordered_names {
            if let Some(toml::Value::Array(arr)) = table.get(*name) {
                let tlds = parse_list(arr, &mut overrides);
                lists.push(NamedList {
                    name: name.to_string(),
                    tlds,
                });
            }
        }

        ParsedLists {
            lists,
            whois_overrides: WhoisOverrides { map: overrides },
        }
    })
}


pub fn list_names() -> Vec<&'static str> {
    parsed_lists()
        .lists
        .iter()
        .map(|l| l.name.as_str())
        .collect()
}

pub fn default_list_name() -> &'static str {
    list_names().first().copied().unwrap_or("standard")
}

pub fn get_tlds(name: &str) -> Option<Vec<&'static str>> {
    let lower = name.to_lowercase();
    parsed_lists()
        .lists
        .iter()
        .find(|l| l.name == lower)
        .map(|l| l.tlds.iter().map(String::as_str).collect())
}


pub fn get_tlds_or_default(name: &str) -> Vec<&'static str> {
    get_tlds(name).unwrap_or_else(|| get_tlds(default_list_name()).unwrap_or_default())
}


pub fn whois_overrides() -> &'static WhoisOverrides {
    &parsed_lists().whois_overrides
}

pub fn apply_top_tlds(tlds: Vec<&'static str>, top: &[String]) -> Vec<&'static str> {
    let mut result: Vec<&'static str> = Vec::with_capacity(tlds.len());
    // add the top ones in the order specified
    for t in top {
        let lower = t.to_lowercase();
        if let Some(&found) = tlds.iter().find(|&&tld| tld.to_lowercase() == lower) {
            if !result.contains(&found) {
                result.push(found);
            }
        }
    }
    // then add the rest lol
    for tld in &tlds {
        if !result.contains(tld) {
            result.push(tld);
        }
    }
    result
}
