use std::path::PathBuf;

use anyhow::{Context, Result};
use directories::ProjectDirs;
use tokio::fs;

#[derive(Debug, Clone)]
pub struct Paths {
    pub data_dir: PathBuf,
    pub state_file: PathBuf,
    pub socket_file: PathBuf,
    pub log_dir: PathBuf,
}

impl Paths {
    pub fn discover() -> Result<Self> {
        if let Ok(root) = std::env::var("MULTI_BRAIN_HOME") {
            return Ok(Self::from_root(PathBuf::from(root)));
        }
        let dirs = ProjectDirs::from("dev", "gonzih", "multi-brain")
            .context("could not resolve project directories")?;
        Ok(Self::from_root(dirs.data_dir().to_path_buf()))
    }

    pub fn from_root(data_dir: PathBuf) -> Self {
        Self {
            state_file: data_dir.join("state.json"),
            socket_file: data_dir.join("multi-brain.sock"),
            log_dir: data_dir.join("logs"),
            data_dir,
        }
    }

    pub async fn ensure(&self) -> Result<()> {
        fs::create_dir_all(&self.data_dir).await?;
        fs::create_dir_all(&self.log_dir).await?;
        Ok(())
    }
}

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

    #[tokio::test]
    async fn from_root_derives_all_paths_and_ensure_creates_dirs() {
        let dir = tempfile::tempdir().unwrap();
        let paths = Paths::from_root(dir.path().join("brain"));
        assert_eq!(paths.state_file, paths.data_dir.join("state.json"));
        assert_eq!(paths.socket_file, paths.data_dir.join("multi-brain.sock"));
        assert_eq!(paths.log_dir, paths.data_dir.join("logs"));
        paths.ensure().await.unwrap();
        assert!(paths.data_dir.exists());
        assert!(paths.log_dir.exists());
    }

    #[test]
    fn discover_honors_multi_brain_home() {
        let dir = tempfile::tempdir().unwrap();
        unsafe {
            std::env::set_var("MULTI_BRAIN_HOME", dir.path());
        }
        let paths = Paths::discover().unwrap();
        assert_eq!(paths.data_dir, dir.path());
        unsafe {
            std::env::remove_var("MULTI_BRAIN_HOME");
        }
    }

    #[test]
    fn discover_uses_platform_dir_without_multi_brain_home() {
        unsafe {
            std::env::remove_var("MULTI_BRAIN_HOME");
        }
        let paths = Paths::discover().unwrap();
        assert!(paths.data_dir.to_string_lossy().contains("multi-brain"));
    }
}
