use std::{
    fmt::{self, Display, Formatter},
    io,
    process::Command,
};

pub struct VersionedPackage {
    name: String,
    suffix_start: usize,
}

impl VersionedPackage {
    pub fn new(name: String, prefix: &str) -> Self {
        let suffix_start = if name.starts_with(&prefix) {
            prefix.len()
        } else {
            0
        };

        Self { name, suffix_start }
    }

    pub fn resolve(&self, version: &str) -> String {
        format!("php{}{}", version, &self.name[self.suffix_start..])
    }
}

impl Display for VersionedPackage {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "{}", self.name)
    }
}

pub fn find_versioned_packages(version: &str) -> io::Result<Vec<VersionedPackage>> {
    let prefix = format!("php{}", version);

    let output = Command::new("dpkg-query")
        .arg("--show")
        .arg("--showformat=${Package}\t${db:Status-Abbrev}\n")
        .arg(format!("{}*", prefix))
        .output()?;

    let output_text = String::from_utf8(output.stdout).unwrap();

    let mut packages: Vec<VersionedPackage> = output_text
        .split("\n")
        .filter_map(|package| {
            let mut parts = package.split("\t");

            let name = parts.next();
            let status = parts.next().map_or(None, |status| status.chars().next());
            let installed = status == Some('i') || status == Some('h') || status == None;

            return if installed {
                name.map(|name| VersionedPackage::new(name.into(), &prefix))
            } else {
                None
            };
        })
        .collect();

    packages.pop(); // Remove the last empty element

    Ok(packages)
}
