use std::collections::HashMap;

use super::super::models::types::SystemProfile;
use super::normalize_optional;

/// Parse system information from the output of system probe commands.
///
/// This is shared parsing logic used by all `RemoteShell` implementations.
/// It parses `/etc/os-release`, `uname -m`, shell path, and package manager probe results.
pub(crate) fn system_profile_from_outputs(
    os_release: &str,
    arch: &str,
    shell: &str,
    package_manager_probe: &str,
) -> SystemProfile {
    let os_release_map = parse_os_release(os_release);

    let mut profile = SystemProfile {
        os_id: os_release_map.get("ID").cloned(),
        os_version: os_release_map.get("VERSION_ID").cloned(),
        arch: normalize_optional(arch),
        package_manager: normalize_package_manager(package_manager_probe),
        shell: normalize_optional(shell),
    };

    if profile.package_manager.is_none() {
        profile.package_manager = profile
            .os_id
            .as_deref()
            .and_then(default_package_manager_for_os);
    }

    profile
}

fn parse_os_release(content: &str) -> HashMap<String, String> {
    let mut values = HashMap::new();

    for line in content.lines() {
        let trimmed = line.trim();
        if trimmed.is_empty() || trimmed.starts_with('#') {
            continue;
        }

        let Some((key, value)) = trimmed.split_once('=') else {
            continue;
        };

        values.insert(key.trim().to_string(), unquote(value.trim()));
    }

    values
}

fn unquote(value: &str) -> String {
    if value.len() >= 2 && value.starts_with('"') && value.ends_with('"') {
        return value[1..value.len() - 1].to_string();
    }
    if value.len() >= 2 && value.starts_with('\'') && value.ends_with('\'') {
        return value[1..value.len() - 1].to_string();
    }
    value.to_string()
}

pub(crate) fn normalize_package_manager(probe_output: &str) -> Option<String> {
    let trimmed = probe_output.trim();
    if trimmed.is_empty() {
        return None;
    }

    // Match package manager name at the end of the path to avoid matching "apt" in "captain", etc.
    let basename = trimmed.rsplit('/').next().unwrap_or(trimmed);
    if basename == "apt-get" || basename == "apt" {
        Some("apt".to_string())
    } else if basename == "dnf" {
        Some("dnf".to_string())
    } else if basename == "yum" {
        Some("yum".to_string())
    } else if basename == "apk" {
        Some("apk".to_string())
    } else {
        Some(trimmed.to_string())
    }
}

fn default_package_manager_for_os(os_id: &str) -> Option<String> {
    match os_id {
        "ubuntu" | "debian" => Some("apt".to_string()),
        "centos" | "rhel" | "rocky" | "almalinux" | "fedora" => Some("dnf".to_string()),
        "alpine" => Some("apk".to_string()),
        _ => None,
    }
}

#[cfg(test)]
mod tests {
    use super::system_profile_from_outputs;

    #[test]
    fn parses_common_linux_profile() {
        let profile = system_profile_from_outputs(
            "ID=ubuntu\nVERSION_ID=\"22.04\"\n",
            "x86_64\n",
            "/bin/bash\n",
            "/usr/bin/apt-get\n",
        );

        assert_eq!(profile.os_id.as_deref(), Some("ubuntu"));
        assert_eq!(profile.os_version.as_deref(), Some("22.04"));
        assert_eq!(profile.arch.as_deref(), Some("x86_64"));
        assert_eq!(profile.package_manager.as_deref(), Some("apt"));
        assert_eq!(profile.shell.as_deref(), Some("/bin/bash"));
    }

    #[test]
    fn falls_back_to_os_default_package_manager() {
        let profile =
            system_profile_from_outputs("ID=rocky\nVERSION_ID=\"9.4\"\n", "aarch64", "", "");

        assert_eq!(profile.package_manager.as_deref(), Some("dnf"));
    }

    #[test]
    fn empty_inputs_produce_empty_profile() {
        let profile = system_profile_from_outputs("", "", "", "");
        assert_eq!(profile.os_id, None);
        assert_eq!(profile.os_version, None);
        assert_eq!(profile.arch, None);
        assert_eq!(profile.package_manager, None);
        assert_eq!(profile.shell, None);
    }

    #[test]
    fn handles_comments_in_os_release() {
        let os_release = "# This is a comment\nID=fedora\n# Another comment\nVERSION_ID=\"39\"\n";
        let profile = system_profile_from_outputs(os_release, "x86_64", "/bin/bash", "dnf");
        assert_eq!(profile.os_id.as_deref(), Some("fedora"));
        assert_eq!(profile.os_version.as_deref(), Some("39"));
    }

    #[test]
    fn normalizes_apt_get_to_apt() {
        let profile = system_profile_from_outputs("ID=debian", "", "", "/usr/bin/apt-get");
        assert_eq!(profile.package_manager.as_deref(), Some("apt"));
    }

    #[test]
    fn normalizes_apt_to_apt() {
        let profile = system_profile_from_outputs("ID=debian", "", "", "apt");
        assert_eq!(profile.package_manager.as_deref(), Some("apt"));
    }

    #[test]
    fn normalizes_dnf() {
        let profile = system_profile_from_outputs("ID=fedora", "", "", "/usr/bin/dnf");
        assert_eq!(profile.package_manager.as_deref(), Some("dnf"));
    }

    #[test]
    fn normalizes_yum() {
        let profile = system_profile_from_outputs("ID=centos", "", "", "/usr/bin/yum");
        assert_eq!(profile.package_manager.as_deref(), Some("yum"));
    }

    #[test]
    fn normalizes_apk() {
        let profile = system_profile_from_outputs("ID=alpine", "", "", "/sbin/apk");
        assert_eq!(profile.package_manager.as_deref(), Some("apk"));
    }

    #[test]
    fn debian_defaults_to_apt() {
        let profile = system_profile_from_outputs("ID=debian", "", "", "");
        assert_eq!(profile.package_manager.as_deref(), Some("apt"));
    }

    #[test]
    fn centos_defaults_to_dnf() {
        let profile = system_profile_from_outputs("ID=centos", "", "", "");
        assert_eq!(profile.package_manager.as_deref(), Some("dnf"));
    }

    #[test]
    fn alpine_defaults_to_apk() {
        let profile = system_profile_from_outputs("ID=alpine", "", "", "");
        assert_eq!(profile.package_manager.as_deref(), Some("apk"));
    }

    #[test]
    fn unknown_os_no_default_package_manager() {
        let profile = system_profile_from_outputs("ID=nixos", "", "", "");
        assert_eq!(profile.package_manager, None);
    }

    #[test]
    fn single_quoted_values_are_unquoted() {
        let os_release = "ID='centos'\nVERSION_ID='9'\n";
        let profile = system_profile_from_outputs(os_release, "", "", "");
        assert_eq!(profile.os_id.as_deref(), Some("centos"));
        assert_eq!(profile.os_version.as_deref(), Some("9"));
    }

    #[test]
    fn whitespace_is_trimmed() {
        let profile =
            system_profile_from_outputs("ID=ubuntu\n", "  x86_64  \n", "  /bin/bash  \n", "");
        assert_eq!(profile.arch.as_deref(), Some("x86_64"));
        assert_eq!(profile.shell.as_deref(), Some("/bin/bash"));
    }
}
