//! Storage backend abstraction and implementations
//!
//! Provides multiple storage backends for KOGRAL:
//! - Filesystem: Git-friendly markdown files
//! - Memory: In-memory graph for dev/cache
//! - `SurrealDB`: Scalable database backend (optional)
//!
//! Use [`factory::build`] to construct the appropriate backend from config.

use async_trait::async_trait;

use crate::error::Result;
use crate::models::{Graph, Node};

/// Storage backend trait
///
/// Provides abstraction over different storage implementations (filesystem,
/// `SurrealDB`, memory). All operations are async and return Result<T> for
/// error handling.
#[async_trait]
pub trait Storage: Send + Sync {
    /// Save a complete graph to storage
    ///
    /// Persists the entire graph including all nodes and edges.
    async fn save_graph(&mut self, graph: &Graph) -> Result<()>;

    /// Load a graph from storage
    ///
    /// Returns a fully reconstructed graph with all nodes and relationships.
    async fn load_graph(&self, name: &str) -> Result<Graph>;

    /// Save a single node to storage
    ///
    /// Persists a node, updating if it already exists.
    async fn save_node(&mut self, node: &Node) -> Result<()>;

    /// Load a node by ID from storage
    ///
    /// Searches across all node types (notes, decisions, guidelines, patterns,
    /// journal, execution).
    async fn load_node(&self, graph_name: &str, node_id: &str) -> Result<Node>;

    /// Delete a node from storage
    ///
    /// Removes the node and cleans up relationships.
    async fn delete_node(&mut self, graph_name: &str, node_id: &str) -> Result<()>;

    /// List all graphs in storage
    async fn list_graphs(&self) -> Result<Vec<String>>;

    /// List nodes in a graph, optionally filtered by type
    ///
    /// Returns all nodes of a specific type if `node_type` is provided,
    /// otherwise returns all nodes in the graph.
    async fn list_nodes(&self, graph_name: &str, node_type: Option<&str>) -> Result<Vec<Node>>;
}

/// Blanket `Storage` impl for `Box<dyn Storage>`.
///
/// Enables type-erased composition: `EventingStorage<Box<dyn Storage>>`
/// satisfies `S: Storage + Send` so the factory can wrap any backend
/// with NATS events without knowing the concrete type at compile time.
#[async_trait]
impl Storage for Box<dyn Storage> {
    async fn save_graph(&mut self, graph: &Graph) -> Result<()> {
        (**self).save_graph(graph).await
    }

    async fn load_graph(&self, name: &str) -> Result<Graph> {
        (**self).load_graph(name).await
    }

    async fn save_node(&mut self, node: &Node) -> Result<()> {
        (**self).save_node(node).await
    }

    async fn load_node(&self, graph_name: &str, node_id: &str) -> Result<Node> {
        (**self).load_node(graph_name, node_id).await
    }

    async fn delete_node(&mut self, graph_name: &str, node_id: &str) -> Result<()> {
        (**self).delete_node(graph_name, node_id).await
    }

    async fn list_graphs(&self) -> Result<Vec<String>> {
        (**self).list_graphs().await
    }

    async fn list_nodes(&self, graph_name: &str, node_type: Option<&str>) -> Result<Vec<Node>> {
        (**self).list_nodes(graph_name, node_type).await
    }
}

pub mod factory;
pub mod filesystem;
pub mod memory;

#[cfg(feature = "surrealdb-backend")]
pub mod surrealdb;
