//! Execution context for query evaluation.

use std::collections::HashMap;


use crate::result::{CypherValue, Record};

/// Execution context that holds variable bindings and parameters.
#[derive(Debug, Clone)]
pub struct ExecutionContext {
    /// Variable bindings for the current scope.
    bindings: Vec<HashMap<String, CypherValue>>,
    /// Query parameters.
    parameters: HashMap<String, CypherValue>,
}

impl ExecutionContext {
    /// Create a new empty context.
    pub fn new() -> Self {
        Self {
            bindings: vec![HashMap::new()],
            parameters: HashMap::new(),
        }
    }

    /// Create a context with parameters.
    pub fn with_parameters(parameters: HashMap<String, CypherValue>) -> Self {
        Self {
            bindings: vec![HashMap::new()],
            parameters,
        }
    }

    /// Push a new scope.
    pub fn push_scope(&mut self) {
        self.bindings.push(HashMap::new());
    }

    /// Pop the current scope.
    pub fn pop_scope(&mut self) {
        if self.bindings.len() > 1 {
            self.bindings.pop();
        }
    }

    /// Set a variable in the current scope.
    pub fn set(&mut self, name: impl Into<String>, value: CypherValue) {
        if let Some(scope) = self.bindings.last_mut() {
            scope.insert(name.into(), value);
        }
    }

    /// Get a variable, searching from innermost to outermost scope.
    pub fn get(&self, name: &str) -> Option<&CypherValue> {
        for scope in self.bindings.iter().rev() {
            if let Some(value) = scope.get(name) {
                return Some(value);
            }
        }
        None
    }

    /// Check if a variable exists.
    pub fn contains(&self, name: &str) -> bool {
        self.get(name).is_some()
    }

    /// Get a parameter value.
    pub fn get_parameter(&self, name: &str) -> Option<&CypherValue> {
        self.parameters.get(name)
    }

    /// Set a parameter.
    pub fn set_parameter(&mut self, name: impl Into<String>, value: CypherValue) {
        self.parameters.insert(name.into(), value);
    }

    /// Get all variables in the current scope.
    pub fn current_scope_variables(&self) -> Vec<(&String, &CypherValue)> {
        self.bindings
            .last()
            .map(|scope| scope.iter().collect())
            .unwrap_or_default()
    }

    /// Get all visible variables (from all scopes, inner shadows outer).
    pub fn all_variables(&self) -> HashMap<String, &CypherValue> {
        let mut result = HashMap::new();
        for scope in &self.bindings {
            for (k, v) in scope {
                result.insert(k.clone(), v);
            }
        }
        result
    }

    /// Create a record from the current bindings.
    pub fn to_record(&self) -> Record {
        let vars = self.all_variables();
        Record::from_pairs(vars.into_iter().map(|(k, v)| (k, v.clone())))
    }

    /// Load bindings from a record.
    pub fn load_record(&mut self, record: &Record) {
        for (k, v) in record.iter() {
            self.set(k.clone(), v.clone());
        }
    }

    /// Replace current scope bindings with the record's contents.
    /// This respects WITH clause scoping - only variables in the record are kept.
    pub fn replace_bindings_from_record(&mut self, record: &Record) {
        if let Some(scope) = self.bindings.last_mut() {
            scope.clear();
            for (k, v) in record.iter() {
                scope.insert(k.clone(), v.clone());
            }
        }
    }

    /// Clear all bindings but keep parameters.
    pub fn clear_bindings(&mut self) {
        self.bindings = vec![HashMap::new()];
    }
}

impl Default for ExecutionContext {
    fn default() -> Self {
        Self::new()
    }
}

/// A set of records representing intermediate results.
#[derive(Debug, Clone)]
pub struct ResultSet {
    /// The records in this result set.
    pub records: Vec<Record>,
}

impl ResultSet {
    /// Create a new empty result set.
    pub fn new() -> Self {
        Self {
            records: Vec::new(),
        }
    }

    /// Create a result set with a single empty record.
    pub fn single_empty() -> Self {
        Self {
            records: vec![Record::new()],
        }
    }

    /// Create from a vector of records.
    pub fn from_records(records: Vec<Record>) -> Self {
        Self { records }
    }

    /// Add a record.
    pub fn add(&mut self, record: Record) {
        self.records.push(record);
    }

    /// Get the number of records.
    pub fn len(&self) -> usize {
        self.records.len()
    }

    /// Check if empty.
    pub fn is_empty(&self) -> bool {
        self.records.is_empty()
    }

    /// Get all records.
    pub fn records(&self) -> &[Record] {
        &self.records
    }

    /// Iterate over records.
    pub fn iter(&self) -> impl Iterator<Item = &Record> {
        self.records.iter()
    }

    /// Iterate mutably over records.
    pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut Record> {
        self.records.iter_mut()
    }

    /// Get unique records (for DISTINCT).
    pub fn distinct(self) -> Self {
        let mut seen = std::collections::HashSet::new();
        let mut unique = Vec::new();

        for record in self.records {
            let key = format!("{:?}", record);
            if seen.insert(key) {
                unique.push(record);
            }
        }

        Self { records: unique }
    }

    /// Merge with another result set (cross product).
    /// Only merges records where shared variables have the same value.
    pub fn cross_join(self, other: ResultSet) -> Self {
        if self.is_empty() {
            return other;
        }
        if other.is_empty() {
            return self;
        }

        let mut result = Vec::new();
        for r1 in &self.records {
            for r2 in &other.records {
                // Check if records are compatible (shared variables have same values)
                let mut compatible = true;
                for (key, val2) in r2.iter() {
                    // Skip internal keys (start with __)
                    if key.starts_with("__") {
                        continue;
                    }
                    if let Some(val1) = r1.get(key) {
                        // Variable exists in both records - must match
                        if val1 != val2 {
                            compatible = false;
                            break;
                        }
                    }
                }

                if compatible {
                    let mut merged = r1.clone();
                    merged.merge(r2.clone());
                    result.push(merged);
                }
            }
        }

        Self { records: result }
    }

    /// Left outer join with another result set.
    pub fn left_join<F>(self, other: ResultSet, join_condition: F) -> Self
    where
        F: Fn(&Record, &Record) -> bool,
    {
        let mut result = Vec::new();

        for left in &self.records {
            let mut matched = false;
            for right in &other.records {
                if join_condition(left, right) {
                    let mut merged = left.clone();
                    merged.merge(right.clone());
                    result.push(merged);
                    matched = true;
                }
            }
            if !matched {
                // Include left record with nulls for right columns
                result.push(left.clone());
            }
        }

        Self { records: result }
    }
}

impl Default for ResultSet {
    fn default() -> Self {
        Self::new()
    }
}

impl From<Vec<Record>> for ResultSet {
    fn from(records: Vec<Record>) -> Self {
        Self { records }
    }
}

impl IntoIterator for ResultSet {
    type Item = Record;
    type IntoIter = std::vec::IntoIter<Record>;

    fn into_iter(self) -> Self::IntoIter {
        self.records.into_iter()
    }
}

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

    #[test]
    fn test_context_scopes() {
        let mut ctx = ExecutionContext::new();
        ctx.set("a", CypherValue::Integer(1));

        ctx.push_scope();
        ctx.set("b", CypherValue::Integer(2));
        ctx.set("a", CypherValue::Integer(10)); // Shadows outer

        assert_eq!(ctx.get("a"), Some(&CypherValue::Integer(10)));
        assert_eq!(ctx.get("b"), Some(&CypherValue::Integer(2)));

        ctx.pop_scope();

        assert_eq!(ctx.get("a"), Some(&CypherValue::Integer(1)));
        assert_eq!(ctx.get("b"), None);
    }

    #[test]
    fn test_result_set_cross_join() {
        let mut rs1 = ResultSet::new();
        rs1.add(Record::from_pairs(vec![("a".to_string(), CypherValue::Integer(1))]));
        rs1.add(Record::from_pairs(vec![("a".to_string(), CypherValue::Integer(2))]));

        let mut rs2 = ResultSet::new();
        rs2.add(Record::from_pairs(vec![("b".to_string(), CypherValue::Integer(3))]));
        rs2.add(Record::from_pairs(vec![("b".to_string(), CypherValue::Integer(4))]));

        let joined = rs1.cross_join(rs2);
        assert_eq!(joined.len(), 4);
    }

    #[test]
    fn test_result_set_distinct() {
        let mut rs = ResultSet::new();
        rs.add(Record::from_pairs(vec![("a".to_string(), CypherValue::Integer(1))]));
        rs.add(Record::from_pairs(vec![("a".to_string(), CypherValue::Integer(1))]));
        rs.add(Record::from_pairs(vec![("a".to_string(), CypherValue::Integer(2))]));

        let distinct = rs.distinct();
        assert_eq!(distinct.len(), 2);
    }
}
