//! Persistent storage for scope overrides.
//!
//! Stores scope overrides in a JSON file for persistence across sessions.

use std::path::{Path, PathBuf};

use crate::error::AphoriaError;
use crate::scope::{ScopeId, ScopeOverride};

/// Directory name for scope data within .aphoria.
const SCOPE_DIR: &str = "scope";

/// Filename for overrides storage.
const OVERRIDES_FILE: &str = "overrides.json";

/// Persistent storage for scope overrides.
///
/// Stores overrides in `.aphoria/scope/overrides.json`.
pub struct OverrideStore {
    path: PathBuf,
    overrides: Vec<ScopeOverride>,
}

impl OverrideStore {
    /// Open or create the override store.
    ///
    /// # Arguments
    ///
    /// * `base_dir` - Base directory (typically `.aphoria/`)
    pub fn new(base_dir: &Path) -> Result<Self, AphoriaError> {
        let scope_dir = base_dir.join(SCOPE_DIR);
        std::fs::create_dir_all(&scope_dir).map_err(|e| {
            AphoriaError::Config(format!("Failed to create scope directory: {}", e))
        })?;

        let path = scope_dir.join(OVERRIDES_FILE);
        let overrides = if path.exists() {
            let content = std::fs::read_to_string(&path).map_err(|e| {
                AphoriaError::Config(format!("Failed to read overrides file: {}", e))
            })?;
            serde_json::from_str(&content)
                .map_err(|e| AphoriaError::Config(format!("Failed to parse overrides: {}", e)))?
        } else {
            Vec::new()
        };

        Ok(Self { path, overrides })
    }

    /// Add a new override.
    ///
    /// If an override for the same scope/concept/predicate exists, it is replaced.
    pub fn add(&mut self, override_record: ScopeOverride) -> Result<(), AphoriaError> {
        // Remove any existing override for the same key
        self.overrides.retain(|o| {
            !(o.scope == override_record.scope
                && o.concept_path == override_record.concept_path
                && o.predicate == override_record.predicate)
        });

        self.overrides.push(override_record);
        self.persist()
    }

    /// Remove an override by concept path.
    ///
    /// Returns true if an override was removed.
    pub fn remove(&mut self, scope: &ScopeId, concept_path: &str) -> Result<bool, AphoriaError> {
        let initial_len = self.overrides.len();
        self.overrides.retain(|o| !(&o.scope == scope && o.concept_path == concept_path));

        if self.overrides.len() < initial_len {
            self.persist()?;
            Ok(true)
        } else {
            Ok(false)
        }
    }

    /// List overrides, optionally filtered by scope.
    ///
    /// # Arguments
    ///
    /// * `scope` - If Some, only return overrides for this scope
    /// * `include_expired` - If true, include expired overrides
    pub fn list(&self, scope: Option<&ScopeId>, include_expired: bool) -> Vec<&ScopeOverride> {
        self.overrides
            .iter()
            .filter(|o| scope.map_or(true, |s| &o.scope == s))
            .filter(|o| include_expired || o.is_active())
            .collect()
    }

    /// List overrides for a scope and its ancestors (inheritance chain).
    pub fn list_with_inheritance(
        &self,
        chain: &[ScopeId],
        include_expired: bool,
    ) -> Vec<&ScopeOverride> {
        self.overrides
            .iter()
            .filter(|o| chain.contains(&o.scope))
            .filter(|o| include_expired || o.is_active())
            .collect()
    }

    /// Get a specific override by scope and concept path.
    pub fn get(&self, scope: &ScopeId, concept_path: &str) -> Option<&ScopeOverride> {
        self.overrides
            .iter()
            .find(|o| &o.scope == scope && o.concept_path == concept_path && o.is_active())
    }

    /// Get the count of active overrides.
    pub fn active_count(&self) -> usize {
        self.overrides.iter().filter(|o| o.is_active()).count()
    }

    /// Get the count of expired overrides.
    pub fn expired_count(&self) -> usize {
        self.overrides.iter().filter(|o| o.is_expired()).count()
    }

    /// Persist overrides to disk.
    fn persist(&self) -> Result<(), AphoriaError> {
        let json = serde_json::to_string_pretty(&self.overrides)
            .map_err(|e| AphoriaError::Config(format!("Failed to serialize overrides: {}", e)))?;

        std::fs::write(&self.path, json)
            .map_err(|e| AphoriaError::Config(format!("Failed to write overrides file: {}", e)))?;

        Ok(())
    }

    /// Get the path to the overrides file.
    pub fn path(&self) -> &Path {
        &self.path
    }
}

/// Get the default override store directory.
pub fn override_store_dir() -> PathBuf {
    dirs::home_dir().unwrap_or_else(|| PathBuf::from(".")).join(".aphoria")
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::scope::OverrideValue;
    use chrono::{Duration, Utc};
    use tempfile::TempDir;

    fn create_test_store() -> (TempDir, OverrideStore) {
        let temp_dir = TempDir::new().expect("create temp dir");
        let store = OverrideStore::new(temp_dir.path()).expect("create store");
        (temp_dir, store)
    }

    #[test]
    fn test_add_and_list() {
        let (_temp, mut store) = create_test_store();

        let scope = ScopeId::project("test-project");
        let override_record = ScopeOverride::new(
            scope.clone(),
            "tls/min_version",
            "version",
            OverrideValue::Text("1.3".to_string()),
            "Security requirement",
        );

        store.add(override_record).expect("add override");

        let overrides = store.list(Some(&scope), false);
        assert_eq!(overrides.len(), 1);
        assert_eq!(overrides[0].concept_path, "tls/min_version");
    }

    #[test]
    fn test_remove() {
        let (_temp, mut store) = create_test_store();

        let scope = ScopeId::project("test-project");
        let override_record = ScopeOverride::new(
            scope.clone(),
            "tls/min_version",
            "version",
            OverrideValue::Text("1.3".to_string()),
            "Security requirement",
        );

        store.add(override_record).expect("add override");
        assert_eq!(store.active_count(), 1);

        let removed = store.remove(&scope, "tls/min_version").expect("remove");
        assert!(removed);
        assert_eq!(store.active_count(), 0);
    }

    #[test]
    fn test_expired_filtering() {
        let (_temp, mut store) = create_test_store();

        let scope = ScopeId::project("test-project");

        // Add expired override
        let expired = ScopeOverride::new(
            scope.clone(),
            "old/setting",
            "value",
            OverrideValue::Boolean(true),
            "Temporary",
        )
        .with_expires_at(Utc::now() - Duration::days(1));

        store.add(expired).expect("add expired");

        // Add active override
        let active = ScopeOverride::new(
            scope.clone(),
            "current/setting",
            "value",
            OverrideValue::Boolean(true),
            "Current",
        );

        store.add(active).expect("add active");

        // Without expired
        let active_only = store.list(Some(&scope), false);
        assert_eq!(active_only.len(), 1);

        // With expired
        let all = store.list(Some(&scope), true);
        assert_eq!(all.len(), 2);
    }

    #[test]
    fn test_persistence() {
        let temp_dir = TempDir::new().expect("create temp dir");

        // Create store and add override
        {
            let mut store = OverrideStore::new(temp_dir.path()).expect("create store");
            let scope = ScopeId::project("test-project");
            let override_record = ScopeOverride::new(
                scope,
                "test/path",
                "predicate",
                OverrideValue::Integer(42),
                "Test",
            );
            store.add(override_record).expect("add override");
        }

        // Reopen store and verify persistence
        {
            let store = OverrideStore::new(temp_dir.path()).expect("reopen store");
            assert_eq!(store.active_count(), 1);
        }
    }

    #[test]
    fn test_replace_existing() {
        let (_temp, mut store) = create_test_store();

        let scope = ScopeId::project("test-project");

        // Add initial override
        let initial = ScopeOverride::new(
            scope.clone(),
            "tls/min_version",
            "version",
            OverrideValue::Text("1.2".to_string()),
            "Initial",
        );
        store.add(initial).expect("add initial");

        // Add replacement
        let replacement = ScopeOverride::new(
            scope.clone(),
            "tls/min_version",
            "version",
            OverrideValue::Text("1.3".to_string()),
            "Updated",
        );
        store.add(replacement).expect("add replacement");

        // Should only have one override
        let overrides = store.list(Some(&scope), false);
        assert_eq!(overrides.len(), 1);
        assert_eq!(overrides[0].reason, "Updated");
    }
}
