//! `paths` command -- report resolved directory paths and their existence.

use serde::Serialize;

use crate::{CliError, json::render_json};

/// The six resolved directory paths (`base`/`wal`/`items`/`users`/`creators`/
/// `cache`), projected from a [`tidaldb::Paths`].
///
/// This is the single source of truth for the directory-projection shape: both
/// the `paths` command (here, embedded in [`PathsOutput`]) and the `status`
/// command (which embeds it directly) build it via [`DirsOutput::new`], so the
/// field set and the `Paths` accessor each maps to can never drift between the
/// two surfaces.
#[derive(Serialize)]
pub(crate) struct DirsOutput {
    pub(crate) base: String,
    pub(crate) wal: String,
    pub(crate) items: String,
    pub(crate) users: String,
    pub(crate) creators: String,
    pub(crate) cache: String,
}

impl DirsOutput {
    pub(crate) fn new(paths: &tidaldb::Paths) -> Self {
        Self {
            base: paths.base().display().to_string(),
            wal: paths.wal_dir().display().to_string(),
            items: paths.items_dir().display().to_string(),
            users: paths.users_dir().display().to_string(),
            creators: paths.creators_dir().display().to_string(),
            cache: paths.cache_dir().display().to_string(),
        }
    }
}

/// `paths` command output: the six resolved directory strings (flattened from
/// the shared [`DirsOutput`]) plus a parallel `exists` map for each.
#[derive(Serialize)]
struct PathsOutput {
    #[serde(flatten)]
    dirs: DirsOutput,
    exists: PathExists,
}

#[derive(Serialize)]
#[allow(clippy::struct_excessive_bools)] // a JSON existence report; one bool per on-disk path is the natural shape
struct PathExists {
    base: bool,
    wal: bool,
    items: bool,
    users: bool,
    creators: bool,
    cache: bool,
}

pub(crate) fn run(base: &std::path::Path, pretty: bool) -> Result<(String, i32), CliError> {
    let paths = tidaldb::Paths::new(base);

    let output = PathsOutput {
        dirs: DirsOutput::new(&paths),
        exists: PathExists {
            base: paths.base().exists(),
            wal: paths.wal_dir().exists(),
            items: paths.items_dir().exists(),
            users: paths.users_dir().exists(),
            creators: paths.creators_dir().exists(),
            cache: paths.cache_dir().exists(),
        },
    };

    let rendered = render_json(&output, pretty)?;
    Ok((rendered, 0))
}

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

    /// The shared projection maps each field to the matching `Paths` accessor,
    /// so the `paths` JSON exposes exactly the six engine directories. Guards
    /// against a field silently mapping to the wrong directory after a refactor.
    #[test]
    fn dirs_output_mirrors_paths_accessors() {
        let base = std::path::Path::new("/tmp/tidaldb-paths-test");
        let paths = tidaldb::Paths::new(base);
        let dirs = DirsOutput::new(&paths);

        assert_eq!(dirs.base, paths.base().display().to_string());
        assert_eq!(dirs.wal, paths.wal_dir().display().to_string());
        assert_eq!(dirs.items, paths.items_dir().display().to_string());
        assert_eq!(dirs.users, paths.users_dir().display().to_string());
        assert_eq!(dirs.creators, paths.creators_dir().display().to_string());
        assert_eq!(dirs.cache, paths.cache_dir().display().to_string());
    }
}
