//! Config source that loads configuration from a file in the local directory.
//! (by default `~/.cipherstash/config.toml`).
//!
//! Configuration loaded from this source:
//!
//! - `WORKSPACE_CRN`: The current CRN to use.
//!
//! The source uses the [FileSource] that wraps the [UserConfigSource] type so you can override the default file name and path if you like.
//!
//! ```no_run
//! use cipherstash_client::config::{FileSource, UserConfigSource};
//!
//! // Load the file Workspace CRN from `some/place/myconfig.toml` (if it exists)
//! // NOTE: The second const parameter is `false` because the file is not required.
//! let source: FileSource<UserConfigSource, false> =
//!     FileSource::default()
//!         .with_path("some/place")
//!         .with_filename("myconfig.toml")
//!         .optional();
//! ```
//!
use std::path::{Path, PathBuf};

use super::{cipherstash::NonSensitiveAuthSection, file::IntoFileSource};
use cts_common::Crn;
use serde::{Deserialize, Serialize};

/// Specifies the format for a user's configuration file.
/// Usually located at `~/.cipherstash/config.toml`.
/// See the module-level documentation for more details.
#[derive(Serialize, Deserialize, Debug)]
pub struct UserConfigSource {
    auth: NonSensitiveAuthSection,
}

impl UserConfigSource {
    /// The default file name for the local configuration file.
    pub const DEFAULT_FILE_NAME: &'static str = "config.toml";

    /// The default file path for the local configuration file (relative to the home directory).
    pub const DEFAULT_FILE_PATH: &'static str = ".cipherstash";

    /// Creates a new `LocalConfigSource` with the default file name.
    pub fn new(workspace: Crn) -> Self {
        Self {
            auth: NonSensitiveAuthSection::new(workspace),
        }
    }

    /// Returns the workspace CRN if set.
    pub fn workspace_crn(&self) -> Option<Crn> {
        self.auth.workspace_crn()
    }

    /// Sets the current workspace CRN.
    pub fn set_current_workspace(&mut self, crn: Crn) {
        self.auth.set_workspace_crn(crn);
    }
}

impl IntoFileSource for UserConfigSource {
    fn filename() -> String {
        Self::DEFAULT_FILE_NAME.to_string()
    }

    fn default_path() -> PathBuf {
        dirs::home_dir()
            .unwrap_or_else(|| Path::new(".").to_path_buf())
            .join(Self::DEFAULT_FILE_PATH)
    }

    fn meta(&self) -> Option<&str> {
        None
    }
}

#[cfg(test)]
mod tests {
    use cts_common::{Region, WorkspaceId};
    use tempfile::TempDir;

    use super::*;
    use crate::config::FileSource;

    #[test]
    fn test_save_local_config() -> anyhow::Result<()> {
        let region = Region::aws("us-west-2")?;
        let workspace_id = WorkspaceId::try_from("UDSJ6IU2YX5LEHGG")?;
        let workspace_crn = Crn::new(region, workspace_id);
        let config = UserConfigSource::new(workspace_crn.clone());

        // Save the file to a temporary directory
        let dir = TempDir::new_in(".")?;
        let source: FileSource<UserConfigSource> = FileSource::default().with_path(dir.path());
        assert!(source.save(config).is_ok());

        // Check if the file exists
        let file_path = format!(
            "{}/{}",
            dir.path().display(),
            UserConfigSource::DEFAULT_FILE_NAME
        );
        assert!(std::path::Path::new(&file_path).exists());

        // Load the file and check if the workspace CRN is correct
        assert_eq!(source.load()?.auth.workspace_crn(), Some(workspace_crn));

        Ok(())
    }
}
