use std::collections::HashMap;
use crate::http_client::HttpClient;
use crate::error::Result;
use crate::models::{
    IpGeolocationResponse, IpGeolocationWithConfidenceAreaResponse, IpGeolocationFullResponse,
    CountryInfo, HazardReport, UserRisk, AsnInfoResponse, NetworkByIpResponse,
    TimezoneResponse, UserAgentResponse, CountryByIpResponse,
};

/// IP Geolocation package — 13 endpoints.
#[derive(Clone)]
pub struct IpGeolocationApi {
    http: HttpClient,
}

impl IpGeolocationApi {
    pub fn new(http: HttpClient) -> Self { Self { http } }

    /// Returns geolocation data for an IP address. Pass `None` to use the caller's IP.
    pub fn get(&self, ip: Option<&str>, locality_language: &str) -> Result<IpGeolocationResponse> {
        self.http.get("ip-geolocation", params(ip, locality_language))
    }

    /// Returns geolocation including confidence area polygon(s).
    pub fn get_with_confidence_area(&self, ip: Option<&str>, locality_language: &str) -> Result<IpGeolocationWithConfidenceAreaResponse> {
        self.http.get("ip-geolocation-with-confidence", params(ip, locality_language))
    }

    /// Returns full geolocation including confidence area and hazard report.
    pub fn get_full(&self, ip: Option<&str>, locality_language: &str) -> Result<IpGeolocationFullResponse> {
        self.http.get("ip-geolocation-full", params(ip, locality_language))
    }

    /// Returns country information for an IP address.
    pub fn get_country_by_ip(&self, ip: Option<&str>, locality_language: &str) -> Result<CountryByIpResponse> {
        self.http.get("country-by-ip", params(ip, locality_language))
    }

    /// Returns detailed information about a country by ISO code.
    pub fn get_country_info(&self, country_code: &str, locality_language: &str) -> Result<CountryInfo> {
        let mut p = HashMap::new();
        p.insert("code".to_string(), country_code.to_string());
        p.insert("localityLanguage".to_string(), locality_language.to_string());
        self.http.get("country-info", p)
    }

    /// Returns a list of all countries with full details.
    pub fn get_all_countries(&self, locality_language: &str) -> Result<Vec<CountryInfo>> {
        let mut p = HashMap::new();
        p.insert("localityLanguage".to_string(), locality_language.to_string());
        self.http.get("countries", p)
    }

    /// Returns a detailed hazard and threat report for an IP address.
    pub fn get_hazard_report(&self, ip: Option<&str>) -> Result<HazardReport> {
        self.http.get("hazard-report", opt_ip(ip))
    }

    /// Returns a risk assessment suitable for e-commerce and sign-up flows.
    pub fn get_user_risk(&self, ip: Option<&str>) -> Result<UserRisk> {
        self.http.get("user-risk", opt_ip(ip))
    }

    /// Returns short ASN information (no peers/transit lists).
    pub fn get_asn_info(&self, asn: &str, locality_language: &str) -> Result<AsnInfoResponse> {
        let mut p = HashMap::new();
        p.insert("asn".to_string(), asn.to_string());
        p.insert("localityLanguage".to_string(), locality_language.to_string());
        self.http.get("asn-info", p)
    }

    /// Returns detailed network information for an IP address.
    pub fn get_network_by_ip(&self, ip: &str, locality_language: &str) -> Result<NetworkByIpResponse> {
        let mut p = HashMap::new();
        p.insert("ip".to_string(), ip.to_string());
        p.insert("localityLanguage".to_string(), locality_language.to_string());
        self.http.get("network-by-ip", p)
    }

    /// Returns timezone information for an IANA timezone ID (e.g. "Australia/Sydney").
    pub fn get_timezone_by_iana_id(&self, iana_time_zone_id: &str) -> Result<TimezoneResponse> {
        let mut p = HashMap::new();
        p.insert("timeZoneId".to_string(), iana_time_zone_id.to_string());
        self.http.get("timezone-info", p)
    }

    /// Returns timezone information for an IP address.
    pub fn get_timezone_by_ip(&self, ip: Option<&str>) -> Result<TimezoneResponse> {
        self.http.get("timezone-by-ip", opt_ip(ip))
    }

    /// Parses a User-Agent string into device, OS and browser info.
    pub fn parse_user_agent(&self, user_agent_string: &str) -> Result<UserAgentResponse> {
        let mut p = HashMap::new();
        p.insert("userAgentRaw".to_string(), user_agent_string.to_string());
        self.http.get("user-agent-info", p)
    }
}

fn params(ip: Option<&str>, locality_language: &str) -> HashMap<String, String> {
    let mut p = HashMap::new();
    if let Some(ip) = ip { p.insert("ip".to_string(), ip.to_string()); }
    p.insert("localityLanguage".to_string(), locality_language.to_string());
    p
}

fn opt_ip(ip: Option<&str>) -> HashMap<String, String> {
    let mut p = HashMap::new();
    if let Some(ip) = ip { p.insert("ip".to_string(), ip.to_string()); }
    p
}
