use anyhow::Result;
use std::net::SocketAddr;
use std::path::Path;

#[derive(Clone)]
pub(crate) struct Paths {
    rows: Vec<PathRow>,
}

#[derive(Clone)]
pub(crate) struct PathRow {
    pub(crate) label: &'static str,
    pub(crate) value: String,
}

impl Paths {
    pub(crate) fn new(target: &Path, config: Option<&Path>) -> Result<Self> {
        let mut rows = vec![row("root", target)];
        if let Some(config) = config {
            rows.push(row("config", config));
        }
        rows.push(row("assets", crate::http::assets::dir()?));
        rows.push(row("vendor", crate::http::vendor::dir()?));
        Ok(Self { rows })
    }

    pub(crate) fn with_server(
        mut self,
        actual: SocketAddr,
        local: String,
        network: Option<String>,
    ) -> Self {
        self.rows.push(value("listen", actual.to_string()));
        self.rows.push(value("local", local));
        if let Some(network) = network {
            self.rows.push(value("network", network));
        }
        self
    }

    pub(crate) fn with_gist(mut self, gist: Option<&Path>) -> Self {
        if let Some(gist) = gist {
            self.rows.push(row("gist", gist));
        }
        self
    }

    pub(crate) fn rows(&self) -> &[PathRow] {
        &self.rows
    }

    pub(crate) fn has_gist(&self) -> bool {
        self.rows.iter().any(|row| row.label == "gist")
    }
}

fn row(label: &'static str, path: impl AsRef<Path>) -> PathRow {
    PathRow {
        label,
        value: path.as_ref().to_string_lossy().into_owned(),
    }
}

fn value(label: &'static str, value: String) -> PathRow {
    PathRow { label, value }
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::testutil::TempDir;

    #[test]
    fn rows_include_target_config_assets_and_vendor() {
        let td = TempDir::new("ghrm-runtime-paths");
        let config = td.path().join("config.toml");

        let paths = Paths::new(td.path(), Some(&config)).unwrap();
        let labels: Vec<&str> = paths.rows().iter().map(|row| row.label).collect();

        assert_eq!(labels, vec!["root", "config", "assets", "vendor"]);
    }

    #[test]
    fn rows_include_gist_when_enabled() {
        let td = TempDir::new("ghrm-runtime-gist");
        let gist = td.path().join("gist");

        let paths = Paths::new(td.path(), None).unwrap().with_gist(Some(&gist));
        let labels: Vec<&str> = paths.rows().iter().map(|row| row.label).collect();

        assert!(labels.contains(&"gist"));
        assert!(paths.has_gist());
    }
}
