//! Standard database introspection procedures (db.*).

use std::collections::HashSet;

use crate::error::ExecutionResult;
use crate::graph::{GraphBackend, NodeLike, EdgeLike};
use crate::result::{CypherValue, Record};

use super::{Procedure, ProcedureContext, ProcedureInfo, ProcedureMode, YieldInfo};

/// db.labels() - List all node labels in the graph.
pub struct DbLabels;

impl<G: GraphBackend> Procedure<G> for DbLabels {
    fn info(&self) -> ProcedureInfo {
        ProcedureInfo {
            name: "db.labels".to_string(),
            description: "List all node labels in use".to_string(),
            mode: ProcedureMode::Read,
            parameters: vec![],
            yields: vec![YieldInfo {
                name: "label".to_string(),
                type_name: "STRING".to_string(),
            }],
        }
    }

    fn call(&self, ctx: &ProcedureContext<G>) -> ExecutionResult<Vec<Record>> {
        let mut labels = HashSet::new();

        // Collect all labels from all nodes
        for node in ctx.graph.all_nodes() {
            for label in node.labels() {
                labels.insert(label.to_string());
            }
        }

        // Convert to sorted vector for consistent output
        let mut label_vec: Vec<String> = labels.into_iter().collect();
        label_vec.sort();

        // Return as records
        let mut results = Vec::new();
        for label in label_vec {
            let mut record = Record::new();
            record.add("label", CypherValue::String(label));
            results.push(record);
        }

        Ok(results)
    }
}

/// db.relationshipTypes() - List all relationship types in the graph.
pub struct DbRelationshipTypes;

impl<G: GraphBackend> Procedure<G> for DbRelationshipTypes {
    fn info(&self) -> ProcedureInfo {
        ProcedureInfo {
            name: "db.relationshipTypes".to_string(),
            description: "List all relationship types in use".to_string(),
            mode: ProcedureMode::Read,
            parameters: vec![],
            yields: vec![YieldInfo {
                name: "relationshipType".to_string(),
                type_name: "STRING".to_string(),
            }],
        }
    }

    fn call(&self, ctx: &ProcedureContext<G>) -> ExecutionResult<Vec<Record>> {
        let mut types = HashSet::new();

        // Collect all relationship types
        for edge in ctx.graph.all_edges() {
            types.insert(edge.rel_type().to_string());
        }

        // Convert to sorted vector for consistent output
        let mut type_vec: Vec<String> = types.into_iter().collect();
        type_vec.sort();

        // Return as records
        let mut results = Vec::new();
        for rel_type in type_vec {
            let mut record = Record::new();
            record.add("relationshipType", CypherValue::String(rel_type));
            results.push(record);
        }

        Ok(results)
    }
}

/// db.propertyKeys() - List all property keys in the graph.
pub struct DbPropertyKeys;

impl<G: GraphBackend> Procedure<G> for DbPropertyKeys {
    fn info(&self) -> ProcedureInfo {
        ProcedureInfo {
            name: "db.propertyKeys".to_string(),
            description: "List all property keys in use".to_string(),
            mode: ProcedureMode::Read,
            parameters: vec![],
            yields: vec![YieldInfo {
                name: "propertyKey".to_string(),
                type_name: "STRING".to_string(),
            }],
        }
    }

    fn call(&self, ctx: &ProcedureContext<G>) -> ExecutionResult<Vec<Record>> {
        let mut keys = HashSet::new();

        // Collect property keys from nodes
        for node in ctx.graph.all_nodes() {
            for (key, _) in node.properties() {
                keys.insert(key.to_string());
            }
        }

        // Collect property keys from relationships
        for edge in ctx.graph.all_edges() {
            for (key, _) in edge.properties() {
                keys.insert(key.to_string());
            }
        }

        // Convert to sorted vector for consistent output
        let mut key_vec: Vec<String> = keys.into_iter().collect();
        key_vec.sort();

        // Return as records
        let mut results = Vec::new();
        for key in key_vec {
            let mut record = Record::new();
            record.add("propertyKey", CypherValue::String(key));
            results.push(record);
        }

        Ok(results)
    }
}
