use std::collections::HashMap;

use rpki::data_model::tal::Tal;
use rpki::sync::rrdp::Fetcher;
use rpki::validation::from_tal::{FromTalError, discover_root_ca_instance_from_tal_url};

#[derive(Default)]
struct MapFetcher {
    map: HashMap<String, Result<Vec<u8>, String>>,
}

impl Fetcher for MapFetcher {
    fn fetch(&self, uri: &str) -> Result<Vec<u8>, String> {
        self.map
            .get(uri)
            .cloned()
            .unwrap_or_else(|| Err(format!("no fixture for uri: {uri}")))
    }
}

#[test]
fn discover_root_ca_instance_from_tal_url_succeeds_with_apnic_fixtures() {
    let tal_url = "https://example.test/apnic.tal";
    let tal_bytes =
        std::fs::read("tests/fixtures/tal/apnic-rfc7730-https.tal").expect("read tal fixture");
    let tal = Tal::decode_bytes(&tal_bytes).expect("decode tal fixture");
    let ta_der = std::fs::read("tests/fixtures/ta/apnic-ta.cer").expect("read ta fixture");

    let mut fetcher = MapFetcher::default();
    fetcher
        .map
        .insert(tal_url.to_string(), Ok(tal_bytes.clone()));
    for u in &tal.ta_uris {
        fetcher
            .map
            .insert(u.as_str().to_string(), Ok(ta_der.clone()));
    }

    let out = discover_root_ca_instance_from_tal_url(&fetcher, tal_url).expect("discover root");
    assert_eq!(out.tal_url.as_deref(), Some(tal_url));
    assert!(!out.ca_instance.rsync_base_uri.is_empty());
    assert!(!out.ca_instance.manifest_rsync_uri.is_empty());
    assert!(!out.ca_instance.publication_point_rsync_uri.is_empty());
}

#[test]
fn discover_root_ca_instance_from_tal_url_returns_ta_fetch_error_when_all_candidates_fail() {
    let tal_url = "https://example.test/apnic.tal";
    let tal_bytes =
        std::fs::read("tests/fixtures/tal/apnic-rfc7730-https.tal").expect("read tal fixture");
    let tal = Tal::decode_bytes(&tal_bytes).expect("decode tal fixture");

    let mut fetcher = MapFetcher::default();
    fetcher.map.insert(tal_url.to_string(), Ok(tal_bytes));
    for u in &tal.ta_uris {
        fetcher.map.insert(
            u.as_str().to_string(),
            Err("simulated TA fetch failure".to_string()),
        );
    }

    let err = discover_root_ca_instance_from_tal_url(&fetcher, tal_url).unwrap_err();
    match err {
        FromTalError::TaFetch(s) => {
            assert!(s.contains("fetch"), "{s}");
        }
        other => panic!("unexpected error: {other}"),
    }
}
