use std::path::PathBuf;

use crate::types::ModelId;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct AppPaths {
    app_data_dir: PathBuf,
}

impl AppPaths {
    pub fn current() -> Self {
        Self {
            app_data_dir: resolve_app_data_dir(),
        }
    }

    pub fn app_data_dir(&self) -> PathBuf {
        self.app_data_dir.clone()
    }

    pub fn database_path(&self) -> PathBuf {
        self.app_data_dir.join("kon.db")
    }

    pub fn recordings_dir(&self) -> PathBuf {
        self.app_data_dir.join("recordings")
    }

    pub fn crashes_dir(&self) -> PathBuf {
        self.app_data_dir.join("crashes")
    }

    pub fn logs_dir(&self) -> PathBuf {
        self.app_data_dir.join("logs")
    }

    pub fn diagnostic_reports_dir(&self) -> PathBuf {
        self.app_data_dir.join("diagnostic-reports")
    }

    pub fn models_dir(&self) -> PathBuf {
        self.app_data_dir.join("models")
    }

    pub fn speech_model_dir(&self, id: &ModelId) -> PathBuf {
        self.models_dir().join(id.as_str())
    }

    pub fn llm_models_dir(&self) -> PathBuf {
        self.models_dir().join("llm")
    }

    pub fn migration_sentinel(&self, name: &str) -> PathBuf {
        self.app_data_dir.join(format!(".{name}.sentinel"))
    }
}

pub fn app_paths() -> AppPaths {
    AppPaths::current()
}

pub fn app_data_dir() -> PathBuf {
    app_paths().app_data_dir()
}

fn resolve_app_data_dir() -> PathBuf {
    #[cfg(target_os = "windows")]
    {
        let local_app_data = std::env::var("LOCALAPPDATA").unwrap_or_else(|_| ".".to_string());
        return PathBuf::from(local_app_data).join("kon");
    }

    #[cfg(target_os = "macos")]
    {
        let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
        return PathBuf::from(home)
            .join("Library")
            .join("Application Support")
            .join("Kon");
    }

    #[cfg(target_os = "linux")]
    {
        let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
        let legacy = PathBuf::from(&home).join(".kon");
        if legacy.exists() {
            return legacy;
        }
        if let Ok(xdg) = std::env::var("XDG_DATA_HOME") {
            if !xdg.is_empty() {
                return PathBuf::from(xdg).join("kon");
            }
        }
        PathBuf::from(home).join(".local").join("share").join("kon")
    }

    #[cfg(not(any(target_os = "windows", target_os = "macos", target_os = "linux")))]
    {
        let home = std::env::var("HOME").unwrap_or_else(|_| "/tmp".to_string());
        PathBuf::from(home).join(".kon")
    }
}

#[cfg(test)]
mod tests {
    use super::AppPaths;
    use crate::types::ModelId;
    use std::path::PathBuf;

    #[test]
    fn derives_all_paths_from_one_base() {
        let paths = AppPaths {
            app_data_dir: PathBuf::from("/tmp/kon-test"),
        };
        assert_eq!(paths.database_path(), PathBuf::from("/tmp/kon-test/kon.db"));
        assert_eq!(
            paths.speech_model_dir(&ModelId::new("whisper-base-en")),
            PathBuf::from("/tmp/kon-test/models/whisper-base-en")
        );
        assert_eq!(
            paths.llm_models_dir(),
            PathBuf::from("/tmp/kon-test/models/llm")
        );
    }
}
