//! Configuration persistence - loading and saving settings.

use std::fs;
use std::io::{self, Read, Write};
use std::path::PathBuf;

use tracing::{debug, info, warn};

use super::{get_config_dir, Settings, CONFIG_FILE_NAME};
use crate::error::{ConfigError, Result};

/// Configuration persistence handler.
pub struct ConfigPersistence {
    config_path: PathBuf,
}

impl ConfigPersistence {
    /// Create a new persistence handler with the given config path.
    pub fn new(config_path: PathBuf) -> Self {
        Self { config_path }
    }

    /// Create a persistence handler using the default config location.
    pub fn default_location() -> io::Result<Self> {
        let config_dir = get_config_dir()
            .ok_or_else(|| io::Error::new(io::ErrorKind::NotFound, "Config directory not found"))?;

        Ok(Self::new(config_dir.join(CONFIG_FILE_NAME)))
    }

    /// Get the configuration file path.
    pub fn config_path(&self) -> &PathBuf {
        &self.config_path
    }

    /// Check if the configuration file exists.
    pub fn exists(&self) -> bool {
        self.config_path.exists()
    }

    /// Load settings from the configuration file.
    pub fn load(&self) -> Result<Settings> {
        if !self.exists() {
            debug!("Config file not found, using defaults");
            return Ok(Settings::default());
        }

        let mut file = fs::File::open(&self.config_path).map_err(ConfigError::ReadError)?;

        let mut contents = String::new();
        file.read_to_string(&mut contents)
            .map_err(ConfigError::ReadError)?;

        let settings: Settings = toml::from_str(&contents).map_err(ConfigError::from)?;

        info!("Loaded configuration from {:?}", self.config_path);
        Ok(settings)
    }

    /// Save settings to the configuration file.
    pub fn save(&self, settings: &Settings) -> Result<()> {
        // Ensure parent directory exists
        if let Some(parent) = self.config_path.parent() {
            if !parent.exists() {
                fs::create_dir_all(parent).map_err(ConfigError::WriteError)?;
                debug!("Created config directory: {:?}", parent);
            }
        }

        let contents = toml::to_string_pretty(settings)
            .map_err(|e| ConfigError::InvalidConfig(e.to_string()))?;

        let mut file = fs::File::create(&self.config_path).map_err(ConfigError::WriteError)?;

        file.write_all(contents.as_bytes())
            .map_err(ConfigError::WriteError)?;

        info!("Saved configuration to {:?}", self.config_path);
        Ok(())
    }

    /// Validate settings and apply any necessary migrations.
    pub fn validate_and_migrate(&self, settings: &mut Settings) -> Result<()> {
        // Ensure output directory exists
        if !settings.output.path.exists() {
            fs::create_dir_all(&settings.output.path).map_err(|e| {
                ConfigError::InvalidConfig(format!("Cannot create output directory: {}", e))
            })?;
            debug!("Created output directory: {:?}", settings.output.path);
        }

        // Validate frame rate
        if settings.video.frame_rate == 0 || settings.video.frame_rate > 240 {
            warn!(
                "Invalid frame rate {}, defaulting to 60",
                settings.video.frame_rate
            );
            settings.video.frame_rate = 60;
        }

        // Validate container format
        let valid_containers = ["mp4", "mkv", "mov", "flv"];
        if !valid_containers.contains(&settings.output.container.as_str()) {
            warn!(
                "Invalid container format '{}', defaulting to mp4",
                settings.output.container
            );
            settings.output.container = "mp4".to_string();
        }

        // Validate log level
        let valid_levels = ["trace", "debug", "info", "warn", "error"];
        if !valid_levels.contains(&settings.daemon.log_level.as_str()) {
            warn!(
                "Invalid log level '{}', defaulting to info",
                settings.daemon.log_level
            );
            settings.daemon.log_level = "info".to_string();
        }

        Ok(())
    }
}

/// Load configuration from the default location.
pub fn load_config() -> Result<Settings> {
    let persistence = ConfigPersistence::default_location().map_err(|e| {
        ConfigError::InvalidConfig(format!("Cannot access config directory: {}", e))
    })?;

    let mut settings = persistence.load()?;
    persistence.validate_and_migrate(&mut settings)?;

    Ok(settings)
}

/// Save configuration to the default location.
pub fn save_config(settings: &Settings) -> Result<()> {
    let persistence = ConfigPersistence::default_location().map_err(|e| {
        ConfigError::InvalidConfig(format!("Cannot access config directory: {}", e))
    })?;

    persistence.save(settings)
}

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

    #[test]
    fn test_save_and_load_config() {
        let dir = tempdir().unwrap();
        let config_path = dir.path().join("config.toml");
        let persistence = ConfigPersistence::new(config_path);

        let settings = Settings::default();
        persistence.save(&settings).unwrap();

        assert!(persistence.exists());

        let loaded = persistence.load().unwrap();
        assert_eq!(settings.video.frame_rate, loaded.video.frame_rate);
        assert_eq!(settings.daemon.auto_record, loaded.daemon.auto_record);
    }

    #[test]
    fn test_load_nonexistent_config() {
        let dir = tempdir().unwrap();
        let config_path = dir.path().join("nonexistent.toml");
        let persistence = ConfigPersistence::new(config_path);

        let settings = persistence.load().unwrap();
        assert_eq!(settings.video.frame_rate, 60);
    }
}
