use crate::reactive::Binding;
use indexmap::IndexMap;
use serde::{Deserialize, Serialize};
use slotmap::new_key_type;
use std::sync::Arc;

// Stable, unique node identifier for reconciliation
new_key_type! {
    pub struct NodeId;
}

/// IR value - either static or a binding to reactive state
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Value {
    /// Static value (string, number, bool, etc.)
    Static(serde_json::Value),
    /// Binding to state: parsed from ${state.user.name}
    Binding(Binding),
    /// Template string with embedded bindings: "Count: ${state.count}"
    /// Stores the template string and all bindings found within it
    TemplateString {
        template: String,
        bindings: Vec<Binding>,
    },
    /// Action reference: @actions.signIn
    Action(String),
}

/// First-class IR node - distinguishes between regular elements and control flow constructs
/// This provides type-safe pattern matching in the reconciler and clear separation of concerns
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum IRNode {
    /// Regular UI element (Text, Column, Button, etc.)
    Element(Element),

    /// Iteration construct: ForEach(items: ${state.todos}, as: "todo", key: "id") { children }
    /// Renders template children for each item in an array
    ForEach {
        /// The binding to the array in state (e.g., ${state.todos})
        source: Binding,
        /// The variable name for each item (default: "item", can be "todo", "user", etc.)
        item_name: String,
        /// Optional path to use as key for stable reconciliation (e.g., "id")
        key_path: Option<String>,
        /// Template children to repeat for each item
        template: Vec<IRNode>,
        /// Additional props for the container element
        props: Props,
    },

    /// Conditional construct: When(value: ${state.status}) { Case(match: "loading") {...} Else {...} }
    /// Renders matching branch based on evaluated value
    Conditional {
        /// The value to match against (binding or static)
        value: Value,
        /// Branches with patterns to match
        branches: Vec<ConditionalBranch>,
        /// Fallback if no branch matches (Else)
        fallback: Option<Vec<IRNode>>,
    },
}

/// A conditional branch with a pattern and children
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ConditionalBranch {
    /// The pattern to match against the conditional value
    pub pattern: Value,
    /// Children to render if this branch matches
    pub children: Vec<IRNode>,
}

impl IRNode {
    /// Create an Element IRNode
    pub fn element(element: Element) -> Self {
        IRNode::Element(element)
    }

    /// Create a ForEach IRNode
    pub fn for_each(
        source: Binding,
        item_name: impl Into<String>,
        key_path: Option<String>,
        template: Vec<IRNode>,
        props: Props,
    ) -> Self {
        IRNode::ForEach {
            source,
            item_name: item_name.into(),
            key_path,
            template,
            props,
        }
    }

    /// Create a Conditional IRNode (When/If)
    pub fn conditional(
        value: Value,
        branches: Vec<ConditionalBranch>,
        fallback: Option<Vec<IRNode>>,
    ) -> Self {
        IRNode::Conditional {
            value,
            branches,
            fallback,
        }
    }

    /// Get the element if this is an Element variant
    pub fn as_element(&self) -> Option<&Element> {
        match self {
            IRNode::Element(e) => Some(e),
            _ => None,
        }
    }

    /// Check if this is a ForEach node
    pub fn is_for_each(&self) -> bool {
        matches!(self, IRNode::ForEach { .. })
    }

    /// Check if this is a Conditional node
    pub fn is_conditional(&self) -> bool {
        matches!(self, IRNode::Conditional { .. })
    }
}

impl ConditionalBranch {
    /// Create a new conditional branch
    pub fn new(pattern: Value, children: Vec<IRNode>) -> Self {
        Self { pattern, children }
    }
}

/// Properties map type (underlying storage)
pub type PropsMap = IndexMap<String, Value>;

/// Arc-wrapped properties for O(1) clone with copy-on-write semantics.
///
/// This enables efficient Element cloning during reconciliation while
/// preserving the ability to modify props when needed via `make_mut()`.
#[derive(Debug, Clone)]
pub struct Props(Arc<PropsMap>);

impl Props {
    /// Create empty props
    pub fn new() -> Self {
        Props(Arc::new(IndexMap::new()))
    }

    /// Create props from an IndexMap
    pub fn from_map(map: PropsMap) -> Self {
        Props(Arc::new(map))
    }

    /// Get mutable access to props (copy-on-write)
    /// If this is the only reference, mutates in place.
    /// Otherwise, clones the inner map first.
    pub fn make_mut(&mut self) -> &mut PropsMap {
        Arc::make_mut(&mut self.0)
    }

    /// Insert a key-value pair (uses COW internally)
    pub fn insert(&mut self, key: String, value: Value) -> Option<Value> {
        self.make_mut().insert(key, value)
    }

    /// Remove a key (uses COW internally)
    pub fn remove(&mut self, key: &str) -> Option<Value> {
        self.make_mut().shift_remove(key)
    }

    /// Get a reference to the inner map
    pub fn inner(&self) -> &PropsMap {
        &self.0
    }
}

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

impl std::ops::Deref for Props {
    type Target = PropsMap;

    fn deref(&self) -> &Self::Target {
        &self.0
    }
}

impl<'a> IntoIterator for &'a Props {
    type Item = (&'a String, &'a Value);
    type IntoIter = indexmap::map::Iter<'a, String, Value>;

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

impl FromIterator<(String, Value)> for Props {
    fn from_iter<I: IntoIterator<Item = (String, Value)>>(iter: I) -> Self {
        Props(Arc::new(iter.into_iter().collect()))
    }
}

// Custom serde - serialize/deserialize as plain IndexMap
impl serde::Serialize for Props {
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: serde::Serializer,
    {
        self.0.serialize(serializer)
    }
}

impl<'de> serde::Deserialize<'de> for Props {
    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
    where
        D: serde::Deserializer<'de>,
    {
        let map = PropsMap::deserialize(deserializer)?;
        Ok(Props(Arc::new(map)))
    }
}

/// Core IR element representing a component instance or primitive
///
/// Uses Arc-wrapped children with im::Vector for O(1) clone performance.
/// This is critical for reconciliation where elements are frequently cloned.
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Element {
    /// Component/element type (e.g., "Column", "Text", "Button")
    pub element_type: String,

    /// Properties passed to this element
    pub props: Props,

    /// Children elements (Arc-wrapped in im::Vector for O(1) structural sharing)
    #[serde(with = "arc_element_vec")]
    pub children: im::Vector<Arc<Element>>,

    /// Optional key for reconciliation (from user or auto-generated)
    pub key: Option<String>,
}

/// Custom serde for im::Vector<Arc<Element>> - serializes as plain Vec
mod arc_element_vec {
    use super::*;
    use serde::{Deserializer, Serializer};

    pub fn serialize<S>(vec: &im::Vector<Arc<Element>>, serializer: S) -> Result<S::Ok, S::Error>
    where
        S: Serializer,
    {
        use serde::ser::SerializeSeq;
        let mut seq = serializer.serialize_seq(Some(vec.len()))?;
        for elem in vec.iter() {
            seq.serialize_element(elem.as_ref())?;
        }
        seq.end()
    }

    pub fn deserialize<'de, D>(deserializer: D) -> Result<im::Vector<Arc<Element>>, D::Error>
    where
        D: Deserializer<'de>,
    {
        let vec: Vec<Element> = Vec::deserialize(deserializer)?;
        Ok(vec.into_iter().map(Arc::new).collect())
    }
}

impl Element {
    pub fn new(element_type: impl Into<String>) -> Self {
        Self {
            element_type: element_type.into(),
            props: Props::new(),
            children: im::Vector::new(),
            key: None,
        }
    }

    pub fn with_prop(mut self, key: impl Into<String>, value: Value) -> Self {
        self.props.insert(key.into(), value);
        self
    }

    pub fn with_child(mut self, child: Element) -> Self {
        self.children.push_back(Arc::new(child));
        self
    }

    /// Add an Arc-wrapped child (avoids extra Arc allocation if already wrapped)
    pub fn with_arc_child(mut self, child: Arc<Element>) -> Self {
        self.children.push_back(child);
        self
    }

    pub fn with_key(mut self, key: impl Into<String>) -> Self {
        self.key = Some(key.into());
        self
    }
}
