//! Query result records.

use std::fmt;

use indexmap::IndexMap;

use super::CypherValue;

/// A single record (row) in query results.
#[derive(Debug, Clone, PartialEq)]
pub struct Record {
    /// The column names.
    columns: Vec<String>,
    /// The values, keyed by column name.
    values: IndexMap<String, CypherValue>,
}

impl Record {
    /// Create a new empty record.
    pub fn new() -> Self {
        Self {
            columns: Vec::new(),
            values: IndexMap::new(),
        }
    }

    /// Create a record from columns and values.
    pub fn from_pairs(pairs: impl IntoIterator<Item = (String, CypherValue)>) -> Self {
        let mut columns = Vec::new();
        let mut values = IndexMap::new();
        for (k, v) in pairs {
            columns.push(k.clone());
            values.insert(k, v);
        }
        Self { columns, values }
    }

    /// Add a value to the record.
    pub fn add(&mut self, key: impl Into<String>, value: CypherValue) {
        let key = key.into();
        if !self.values.contains_key(&key) {
            self.columns.push(key.clone());
        }
        self.values.insert(key, value);
    }

    /// Get a value by column name.
    pub fn get(&self, key: &str) -> Option<&CypherValue> {
        self.values.get(key)
    }

    /// Get a value by index.
    pub fn get_by_index(&self, index: usize) -> Option<&CypherValue> {
        self.columns
            .get(index)
            .and_then(|key| self.values.get(key))
    }

    /// Get the column names.
    pub fn columns(&self) -> &[String] {
        &self.columns
    }

    /// Get all values in column order.
    pub fn values(&self) -> Vec<&CypherValue> {
        self.columns
            .iter()
            .filter_map(|k| self.values.get(k))
            .collect()
    }

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

    /// Check if the record is empty.
    pub fn is_empty(&self) -> bool {
        self.columns.is_empty()
    }

    /// Iterate over (column, value) pairs.
    pub fn iter(&self) -> impl Iterator<Item = (&String, &CypherValue)> {
        self.columns.iter().filter_map(|k| self.values.get(k).map(|v| (k, v)))
    }

    /// Merge another record into this one.
    pub fn merge(&mut self, other: Record) {
        for (k, v) in other.values {
            self.add(k, v);
        }
    }

    /// Create a new record with only the specified columns.
    pub fn project(&self, columns: &[String]) -> Record {
        let mut result = Record::new();
        for col in columns {
            if let Some(value) = self.get(col) {
                result.add(col.clone(), value.clone());
            }
        }
        result
    }

    /// Remove a column from the record.
    pub fn remove(&mut self, key: &str) {
        if self.values.swap_remove(key).is_some() {
            self.columns.retain(|k| k != key);
        }
    }
}

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

impl fmt::Display for Record {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{{")?;
        for (i, (k, v)) in self.iter().enumerate() {
            if i > 0 {
                write!(f, ", ")?;
            }
            write!(f, "{}: {}", k, v)?;
        }
        write!(f, "}}")
    }
}

impl IntoIterator for Record {
    type Item = (String, CypherValue);
    type IntoIter = indexmap::map::IntoIter<String, CypherValue>;

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

impl<'a> IntoIterator for &'a Record {
    type Item = (&'a String, &'a CypherValue);
    type IntoIter = std::iter::FilterMap<
        std::slice::Iter<'a, String>,
        fn(&'a String) -> Option<(&'a String, &'a CypherValue)>,
    >;

    fn into_iter(self) -> Self::IntoIter {
        // This is a workaround; normally you'd store the values reference
        // For now, use iter() method
        unimplemented!("Use record.iter() instead")
    }
}

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

    #[test]
    fn test_record_creation() {
        let mut record = Record::new();
        record.add("name", CypherValue::String("Alice".to_string()));
        record.add("age", CypherValue::Integer(30));

        assert_eq!(record.len(), 2);
        assert_eq!(
            record.get("name"),
            Some(&CypherValue::String("Alice".to_string()))
        );
        assert_eq!(record.get("age"), Some(&CypherValue::Integer(30)));
    }

    #[test]
    fn test_record_get_by_index() {
        let record = Record::from_pairs(vec![
            ("a".to_string(), CypherValue::Integer(1)),
            ("b".to_string(), CypherValue::Integer(2)),
        ]);

        assert_eq!(record.get_by_index(0), Some(&CypherValue::Integer(1)));
        assert_eq!(record.get_by_index(1), Some(&CypherValue::Integer(2)));
        assert_eq!(record.get_by_index(2), None);
    }

    #[test]
    fn test_record_project() {
        let record = Record::from_pairs(vec![
            ("a".to_string(), CypherValue::Integer(1)),
            ("b".to_string(), CypherValue::Integer(2)),
            ("c".to_string(), CypherValue::Integer(3)),
        ]);

        let projected = record.project(&["a".to_string(), "c".to_string()]);
        assert_eq!(projected.len(), 2);
        assert_eq!(projected.get("a"), Some(&CypherValue::Integer(1)));
        assert_eq!(projected.get("c"), Some(&CypherValue::Integer(3)));
        assert_eq!(projected.get("b"), None);
    }
}
