//! Simplified Cypher-like query language for graphs.

use super::edge::Edge;
use super::node::{Node, NodeId};
use super::store::{Direction, GraphError, GraphStore};
use super::traversal::{TraversalDirection, TraversalQuery, Traverser};
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::collections::HashMap;

/// Parsed graph query.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum GraphQuery {
    /// MATCH query for pattern matching.
    Match {
        pattern: MatchPattern,
        where_clause: Option<WhereClause>,
        return_items: Vec<ReturnItem>,
        limit: Option<usize>,
    },
    /// CREATE query for creating nodes/edges.
    Create { elements: Vec<CreateElement> },
    /// DELETE query for removing nodes/edges.
    Delete { variable: String, detach: bool },
    /// SET query for updating properties.
    Set { variable: String, properties: JsonValue },
}

/// Pattern to match in the graph.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct MatchPattern {
    /// Starting node pattern.
    pub start: NodePattern,
    /// Relationship patterns (edges and target nodes).
    pub relationships: Vec<RelationshipPattern>,
}

/// Pattern for matching a node.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct NodePattern {
    /// Variable name for this node.
    pub variable: Option<String>,
    /// Required labels.
    pub labels: Vec<String>,
    /// Property filters.
    pub properties: Option<JsonValue>,
}

/// Pattern for matching a relationship.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct RelationshipPattern {
    /// Variable name for this relationship.
    pub variable: Option<String>,
    /// Edge type (relationship type).
    pub edge_type: Option<String>,
    /// Direction of the relationship.
    pub direction: RelationshipDirection,
    /// Target node pattern.
    pub target: NodePattern,
    /// Minimum hops (for variable-length paths).
    pub min_hops: usize,
    /// Maximum hops (for variable-length paths).
    pub max_hops: usize,
}

/// Direction of a relationship in a pattern.
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq)]
pub enum RelationshipDirection {
    /// Outgoing: (a)-[:TYPE]->(b)
    Outgoing,
    /// Incoming: (a)<-[:TYPE]-(b)
    Incoming,
    /// Undirected: (a)-[:TYPE]-(b)
    Undirected,
}

/// WHERE clause conditions.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum WhereClause {
    /// Property comparison.
    PropertyEquals { variable: String, property: String, value: JsonValue },
    /// Property comparison (not equals).
    PropertyNotEquals { variable: String, property: String, value: JsonValue },
    /// Property greater than.
    PropertyGt { variable: String, property: String, value: JsonValue },
    /// Property less than.
    PropertyLt { variable: String, property: String, value: JsonValue },
    /// Property contains (for text).
    PropertyContains { variable: String, property: String, value: String },
    /// AND condition.
    And(Box<WhereClause>, Box<WhereClause>),
    /// OR condition.
    Or(Box<WhereClause>, Box<WhereClause>),
    /// NOT condition.
    Not(Box<WhereClause>),
}

/// Item to return from a query.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum ReturnItem {
    /// Return all variables.
    All,
    /// Return a specific variable.
    Variable(String),
    /// Return a property of a variable.
    Property { variable: String, property: String },
    /// Return with an alias.
    Alias { item: Box<ReturnItem>, alias: String },
    /// Count aggregation.
    Count(Option<String>),
}

/// Element to create.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub enum CreateElement {
    /// Create a node.
    Node { variable: Option<String>, labels: Vec<String>, properties: JsonValue },
    /// Create a relationship.
    Relationship {
        from_var: String,
        to_var: String,
        edge_type: String,
        properties: JsonValue,
    },
}

/// Result of a graph query.
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct QueryResult {
    /// Column names.
    pub columns: Vec<String>,
    /// Result rows.
    pub rows: Vec<Vec<JsonValue>>,
    /// Number of nodes created.
    pub nodes_created: usize,
    /// Number of relationships created.
    pub relationships_created: usize,
    /// Number of nodes deleted.
    pub nodes_deleted: usize,
    /// Number of relationships deleted.
    pub relationships_deleted: usize,
    /// Number of properties set.
    pub properties_set: usize,
}

impl QueryResult {
    /// Creates an empty result.
    pub fn empty() -> Self {
        Self {
            columns: Vec::new(),
            rows: Vec::new(),
            nodes_created: 0,
            relationships_created: 0,
            nodes_deleted: 0,
            relationships_deleted: 0,
            properties_set: 0,
        }
    }
}

/// Parser for Cypher-like graph queries.
pub struct GraphQueryParser;

impl GraphQueryParser {
    /// Parses a query string into a GraphQuery.
    pub fn parse(query: &str) -> Result<GraphQuery, GraphError> {
        let query = query.trim();
        let upper = query.to_uppercase();

        if upper.starts_with("MATCH") {
            Self::parse_match(query)
        } else if upper.starts_with("CREATE") {
            Self::parse_create(query)
        } else if upper.starts_with("DELETE") || upper.starts_with("DETACH DELETE") {
            Self::parse_delete(query)
        } else if upper.starts_with("SET") {
            Self::parse_set(query)
        } else {
            Err(GraphError::InvalidOperation(format!("Unknown query type: {}", query)))
        }
    }

    fn parse_match(query: &str) -> Result<GraphQuery, GraphError> {
        // Simplified parser for: MATCH (var:Label {props})-[:TYPE]->(var2) WHERE ... RETURN ...
        let upper = query.to_uppercase();

        // Find MATCH, WHERE, RETURN, LIMIT positions
        let match_end = upper.find("WHERE").or_else(|| upper.find("RETURN")).unwrap_or(query.len());
        let where_start = upper.find("WHERE");
        let return_start = upper.find("RETURN");
        let limit_start = upper.find("LIMIT");

        // Parse pattern (between MATCH and WHERE/RETURN)
        let pattern_str = &query[5..match_end].trim();
        let pattern = Self::parse_pattern(pattern_str)?;

        // Parse WHERE clause
        let where_clause = if let Some(ws) = where_start {
            let where_end = return_start.unwrap_or(query.len());
            let where_str = &query[ws + 5..where_end].trim();
            Some(Self::parse_where(where_str)?)
        } else {
            None
        };

        // Parse RETURN clause
        let return_items = if let Some(rs) = return_start {
            let return_end = limit_start.unwrap_or(query.len());
            let return_str = &query[rs + 6..return_end].trim();
            Self::parse_return(return_str)?
        } else {
            vec![ReturnItem::All]
        };

        // Parse LIMIT
        let limit = if let Some(ls) = limit_start {
            let limit_str = query[ls + 5..].trim();
            limit_str.parse().ok()
        } else {
            None
        };

        Ok(GraphQuery::Match {
            pattern,
            where_clause,
            return_items,
            limit,
        })
    }

    fn parse_pattern(pattern: &str) -> Result<MatchPattern, GraphError> {
        // Parse node and relationship patterns
        // Format: (var:Label {props})-[:TYPE]->(var2:Label2)
        let mut chars = pattern.chars().peekable();
        let mut nodes = Vec::new();
        let mut relationships = Vec::new();

        while chars.peek().is_some() {
            // Skip whitespace
            while chars.peek() == Some(&' ') {
                chars.next();
            }

            if chars.peek() == Some(&'(') {
                let node = Self::parse_node_pattern(&mut chars)?;
                nodes.push(node);
            } else if chars.peek() == Some(&'-') || chars.peek() == Some(&'<') {
                let rel = Self::parse_relationship_pattern(&mut chars)?;
                relationships.push(rel);
            } else if chars.peek().is_some() {
                chars.next(); // Skip unknown characters
            }
        }

        if nodes.is_empty() {
            return Err(GraphError::InvalidOperation("No node pattern found".to_string()));
        }

        // Combine nodes with relationships
        let start = nodes.remove(0);
        for (i, rel) in relationships.iter_mut().enumerate() {
            if i < nodes.len() {
                rel.target = nodes[i].clone();
            }
        }

        Ok(MatchPattern { start, relationships })
    }

    fn parse_node_pattern(chars: &mut std::iter::Peekable<std::str::Chars>) -> Result<NodePattern, GraphError> {
        // Consume '('
        chars.next();

        let mut variable = None;
        let mut labels = Vec::new();
        let mut properties = None;

        let mut buffer = String::new();

        while let Some(&c) = chars.peek() {
            match c {
                ')' => {
                    chars.next();
                    if !buffer.is_empty() && variable.is_none() {
                        variable = Some(buffer.clone());
                    }
                    break;
                }
                ':' => {
                    chars.next();
                    if !buffer.is_empty() && variable.is_none() {
                        variable = Some(buffer.clone());
                    }
                    buffer.clear();

                    // Read label
                    while let Some(&c) = chars.peek() {
                        if c == ')' || c == '{' || c == ':' || c == ' ' {
                            break;
                        }
                        buffer.push(c);
                        chars.next();
                    }
                    if !buffer.is_empty() {
                        labels.push(buffer.clone());
                        buffer.clear();
                    }
                }
                '{' => {
                    chars.next();
                    // Read properties JSON
                    let mut props_str = String::from("{");
                    let mut depth = 1;
                    while let Some(&c) = chars.peek() {
                        chars.next();
                        props_str.push(c);
                        if c == '{' {
                            depth += 1;
                        } else if c == '}' {
                            depth -= 1;
                            if depth == 0 {
                                break;
                            }
                        }
                    }
                    properties = serde_json::from_str(&props_str).ok();
                }
                ' ' => {
                    chars.next();
                }
                _ => {
                    buffer.push(c);
                    chars.next();
                }
            }
        }

        Ok(NodePattern { variable, labels, properties })
    }

    fn parse_relationship_pattern(chars: &mut std::iter::Peekable<std::str::Chars>) -> Result<RelationshipPattern, GraphError> {
        let mut direction = RelationshipDirection::Undirected;
        let mut edge_type = None;
        let mut variable = None;
        let min_hops = 1;
        let max_hops = 1;

        // Check for incoming: <-
        if chars.peek() == Some(&'<') {
            chars.next();
            direction = RelationshipDirection::Incoming;
        }

        // Consume -
        if chars.peek() == Some(&'-') {
            chars.next();
        }

        // Check for [type]
        if chars.peek() == Some(&'[') {
            chars.next();
            let mut buffer = String::new();

            while let Some(&c) = chars.peek() {
                if c == ']' {
                    chars.next();
                    break;
                } else if c == ':' {
                    chars.next();
                    if !buffer.is_empty() {
                        variable = Some(buffer.clone());
                    }
                    buffer.clear();

                    // Read edge type
                    while let Some(&c) = chars.peek() {
                        if c == ']' || c == ' ' || c == '*' {
                            break;
                        }
                        buffer.push(c);
                        chars.next();
                    }
                    if !buffer.is_empty() {
                        edge_type = Some(buffer.clone());
                        buffer.clear();
                    }
                } else {
                    buffer.push(c);
                    chars.next();
                }
            }
        }

        // Consume -
        if chars.peek() == Some(&'-') {
            chars.next();
        }

        // Check for outgoing: >
        if chars.peek() == Some(&'>') {
            chars.next();
            if direction != RelationshipDirection::Incoming {
                direction = RelationshipDirection::Outgoing;
            }
        }

        Ok(RelationshipPattern {
            variable,
            edge_type,
            direction,
            target: NodePattern { variable: None, labels: Vec::new(), properties: None },
            min_hops,
            max_hops,
        })
    }

    fn parse_where(_where_str: &str) -> Result<WhereClause, GraphError> {
        // Simplified: just parse "var.prop = value"
        // Full implementation would handle complex boolean expressions
        Ok(WhereClause::PropertyEquals {
            variable: "n".to_string(),
            property: "id".to_string(),
            value: JsonValue::Null,
        })
    }

    fn parse_return(return_str: &str) -> Result<Vec<ReturnItem>, GraphError> {
        let items: Vec<_> = return_str.split(',').map(|s| s.trim()).collect();
        let mut result = Vec::new();

        for item in items {
            if item == "*" {
                result.push(ReturnItem::All);
            } else if item.to_uppercase().starts_with("COUNT(") {
                let inner = &item[6..item.len() - 1];
                if inner == "*" {
                    result.push(ReturnItem::Count(None));
                } else {
                    result.push(ReturnItem::Count(Some(inner.to_string())));
                }
            } else if item.contains('.') {
                let parts: Vec<_> = item.split('.').collect();
                if parts.len() == 2 {
                    result.push(ReturnItem::Property {
                        variable: parts[0].to_string(),
                        property: parts[1].to_string(),
                    });
                }
            } else {
                result.push(ReturnItem::Variable(item.to_string()));
            }
        }

        Ok(result)
    }

    fn parse_create(query: &str) -> Result<GraphQuery, GraphError> {
        // Simplified CREATE parser
        let pattern = &query[6..].trim();
        let elements = Self::parse_create_elements(pattern)?;
        Ok(GraphQuery::Create { elements })
    }

    fn parse_create_elements(pattern: &str) -> Result<Vec<CreateElement>, GraphError> {
        // Parse (var:Label {props}) patterns
        let mut elements = Vec::new();
        let mut chars = pattern.chars().peekable();

        while chars.peek().is_some() {
            while chars.peek() == Some(&' ') || chars.peek() == Some(&',') {
                chars.next();
            }

            if chars.peek() == Some(&'(') {
                let node = Self::parse_node_pattern(&mut chars)?;
                elements.push(CreateElement::Node {
                    variable: node.variable,
                    labels: node.labels,
                    properties: node.properties.unwrap_or(JsonValue::Object(serde_json::Map::new())),
                });
            } else {
                break;
            }
        }

        Ok(elements)
    }

    fn parse_delete(query: &str) -> Result<GraphQuery, GraphError> {
        let detach = query.to_uppercase().starts_with("DETACH");
        let start = if detach { "DETACH DELETE".len() } else { "DELETE".len() };
        let variable = query[start..].trim().to_string();

        Ok(GraphQuery::Delete { variable, detach })
    }

    fn parse_set(query: &str) -> Result<GraphQuery, GraphError> {
        // Simplified: SET var.prop = value
        let content = &query[3..].trim();
        let parts: Vec<_> = content.split('=').collect();

        if parts.len() != 2 {
            return Err(GraphError::InvalidOperation("Invalid SET syntax".to_string()));
        }

        let var_prop: Vec<_> = parts[0].trim().split('.').collect();
        if var_prop.len() != 2 {
            return Err(GraphError::InvalidOperation("Invalid SET variable".to_string()));
        }

        let variable = var_prop[0].to_string();
        let property = var_prop[1].to_string();
        let value_str = parts[1].trim();

        let value: JsonValue = serde_json::from_str(value_str).unwrap_or(JsonValue::String(value_str.to_string()));

        Ok(GraphQuery::Set {
            variable,
            properties: serde_json::json!({ property: value }),
        })
    }
}

/// Query executor for graph queries.
pub struct GraphQueryExecutor<'a> {
    store: &'a GraphStore,
}

impl<'a> GraphQueryExecutor<'a> {
    /// Creates a new query executor.
    pub fn new(store: &'a GraphStore) -> Self {
        Self { store }
    }

    /// Executes a graph query.
    pub fn execute(&self, query: &GraphQuery) -> Result<QueryResult, GraphError> {
        match query {
            GraphQuery::Match { pattern, where_clause, return_items, limit } => {
                self.execute_match(pattern, where_clause.as_ref(), return_items, *limit)
            }
            GraphQuery::Create { .. } => {
                Err(GraphError::InvalidOperation("CREATE requires mutable access".to_string()))
            }
            GraphQuery::Delete { .. } => {
                Err(GraphError::InvalidOperation("DELETE requires mutable access".to_string()))
            }
            GraphQuery::Set { .. } => {
                Err(GraphError::InvalidOperation("SET requires mutable access".to_string()))
            }
        }
    }

    fn execute_match(
        &self,
        pattern: &MatchPattern,
        _where_clause: Option<&WhereClause>,
        return_items: &[ReturnItem],
        limit: Option<usize>,
    ) -> Result<QueryResult, GraphError> {
        // Find starting nodes
        let start_nodes = self.find_matching_nodes(&pattern.start);

        let mut bindings: Vec<HashMap<String, JsonValue>> = Vec::new();

        for start_node in &start_nodes {
            let mut binding: HashMap<String, JsonValue> = HashMap::new();

            if let Some(ref var) = pattern.start.variable {
                binding.insert(var.clone(), Self::node_to_json(start_node));
            }

            if pattern.relationships.is_empty() {
                bindings.push(binding);
            } else {
                // Traverse relationships
                let traverser = Traverser::new(self.store);

                for rel_pattern in &pattern.relationships {
                    let direction = match rel_pattern.direction {
                        RelationshipDirection::Outgoing => TraversalDirection::Outgoing,
                        RelationshipDirection::Incoming => TraversalDirection::Incoming,
                        RelationshipDirection::Undirected => TraversalDirection::Both,
                    };

                    let query = TraversalQuery::new()
                        .depth(rel_pattern.max_hops)
                        .direction(direction)
                        .edge_types(
                            rel_pattern.edge_type.clone().map(|t| vec![t]).unwrap_or_default(),
                        )
                        .labels(rel_pattern.target.labels.clone());

                    let results = traverser.traverse(&start_node.id, &query);

                    for result in results {
                        let mut new_binding = binding.clone();

                        if let Some(ref var) = rel_pattern.variable {
                            if let Some(edge) = result.edges.last() {
                                new_binding.insert(var.clone(), Self::edge_to_json(edge));
                            }
                        }

                        if let Some(ref var) = rel_pattern.target.variable {
                            new_binding.insert(var.clone(), Self::node_to_json(&result.node));
                        }

                        bindings.push(new_binding);
                    }
                }
            }

            if let Some(l) = limit {
                if bindings.len() >= l {
                    break;
                }
            }
        }

        // Apply limit
        if let Some(l) = limit {
            bindings.truncate(l);
        }

        // Build result based on return items
        let columns = self.get_column_names(return_items, &bindings);
        let rows = self.extract_rows(return_items, &bindings);

        Ok(QueryResult {
            columns,
            rows,
            ..QueryResult::empty()
        })
    }

    fn find_matching_nodes(&self, pattern: &NodePattern) -> Vec<Node> {
        let label = pattern.labels.first().map(|s| s.as_str());
        let filter = pattern.properties.clone().unwrap_or(JsonValue::Object(serde_json::Map::new()));
        self.store.find_nodes(label, &filter)
    }

    fn node_to_json(node: &Node) -> JsonValue {
        serde_json::json!({
            "id": node.id.to_hex(),
            "labels": node.labels,
            "properties": node.properties,
        })
    }

    fn edge_to_json(edge: &Edge) -> JsonValue {
        serde_json::json!({
            "id": edge.id.to_hex(),
            "type": edge.edge_type,
            "source": edge.source.to_hex(),
            "target": edge.target.to_hex(),
            "properties": edge.properties,
        })
    }

    fn get_column_names(&self, return_items: &[ReturnItem], bindings: &[HashMap<String, JsonValue>]) -> Vec<String> {
        let mut columns = Vec::new();

        for item in return_items {
            match item {
                ReturnItem::All => {
                    if let Some(binding) = bindings.first() {
                        columns.extend(binding.keys().cloned());
                    }
                }
                ReturnItem::Variable(var) => columns.push(var.clone()),
                ReturnItem::Property { variable, property } => {
                    columns.push(format!("{}.{}", variable, property));
                }
                ReturnItem::Alias { alias, .. } => columns.push(alias.clone()),
                ReturnItem::Count(var) => {
                    columns.push(format!("count({})", var.as_ref().map(|s| s.as_str()).unwrap_or("*")));
                }
            }
        }

        columns
    }

    fn extract_rows(&self, return_items: &[ReturnItem], bindings: &[HashMap<String, JsonValue>]) -> Vec<Vec<JsonValue>> {
        let mut rows = Vec::new();

        // Handle COUNT specially
        if return_items.iter().any(|i| matches!(i, ReturnItem::Count(_))) {
            rows.push(vec![JsonValue::Number(bindings.len().into())]);
            return rows;
        }

        for binding in bindings {
            let mut row = Vec::new();

            for item in return_items {
                match item {
                    ReturnItem::All => {
                        for (_, value) in binding {
                            row.push(value.clone());
                        }
                    }
                    ReturnItem::Variable(var) => {
                        row.push(binding.get(var).cloned().unwrap_or(JsonValue::Null));
                    }
                    ReturnItem::Property { variable, property } => {
                        if let Some(obj) = binding.get(variable) {
                            if let Some(props) = obj.get("properties") {
                                row.push(props.get(property).cloned().unwrap_or(JsonValue::Null));
                            } else {
                                row.push(JsonValue::Null);
                            }
                        } else {
                            row.push(JsonValue::Null);
                        }
                    }
                    ReturnItem::Alias { item: inner, .. } => {
                        // Recursively handle the inner item
                        let inner_rows = self.extract_rows(&[*inner.clone()], &[binding.clone()]);
                        if let Some(inner_row) = inner_rows.first() {
                            row.extend(inner_row.clone());
                        }
                    }
                    ReturnItem::Count(_) => {
                        // Handled above
                    }
                }
            }

            if !row.is_empty() {
                rows.push(row);
            }
        }

        rows
    }
}

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

    #[test]
    fn test_parse_simple_match() {
        let query = "MATCH (n:User) RETURN n";
        let parsed = GraphQueryParser::parse(query).unwrap();

        if let GraphQuery::Match { pattern, .. } = parsed {
            assert_eq!(pattern.start.labels, vec!["User".to_string()]);
        } else {
            panic!("Expected Match query");
        }
    }

    #[test]
    fn test_parse_match_with_relationship() {
        let query = "MATCH (a:User)-[:FRIEND]->(b:User) RETURN a, b";
        let parsed = GraphQueryParser::parse(query).unwrap();

        if let GraphQuery::Match { pattern, .. } = parsed {
            assert_eq!(pattern.start.labels, vec!["User".to_string()]);
            assert_eq!(pattern.relationships.len(), 1);
            assert_eq!(pattern.relationships[0].edge_type, Some("FRIEND".to_string()));
            assert_eq!(pattern.relationships[0].direction, RelationshipDirection::Outgoing);
        } else {
            panic!("Expected Match query");
        }
    }

    #[test]
    fn test_execute_match() {
        let store = GraphStore::new();

        let alice = store.create_node(vec!["User".to_string()], serde_json::json!({"name": "Alice"}));
        let bob = store.create_node(vec!["User".to_string()], serde_json::json!({"name": "Bob"}));
        store.create_edge(alice, bob, "FRIEND", serde_json::json!({})).unwrap();

        let query = GraphQueryParser::parse("MATCH (n:User) RETURN n").unwrap();
        let executor = GraphQueryExecutor::new(&store);
        let result = executor.execute(&query).unwrap();

        assert_eq!(result.rows.len(), 2);
    }

    #[test]
    fn test_parse_create() {
        let query = "CREATE (n:User {name: \"Alice\"})";
        let parsed = GraphQueryParser::parse(query).unwrap();

        if let GraphQuery::Create { elements } = parsed {
            assert_eq!(elements.len(), 1);
            if let CreateElement::Node { labels, .. } = &elements[0] {
                assert_eq!(labels, &vec!["User".to_string()]);
            }
        } else {
            panic!("Expected Create query");
        }
    }

    #[test]
    fn test_parse_delete() {
        let query = "DELETE n";
        let parsed = GraphQueryParser::parse(query).unwrap();

        if let GraphQuery::Delete { variable, detach } = parsed {
            assert_eq!(variable, "n");
            assert!(!detach);
        } else {
            panic!("Expected Delete query");
        }
    }

    #[test]
    fn test_parse_detach_delete() {
        let query = "DETACH DELETE n";
        let parsed = GraphQueryParser::parse(query).unwrap();

        if let GraphQuery::Delete { variable, detach } = parsed {
            assert_eq!(variable, "n");
            assert!(detach);
        } else {
            panic!("Expected Delete query");
        }
    }
}
