//! Tree-based deserializer for DOM documents.

use std::borrow::Cow;

use facet_core::{Def, StructKind, Type, UserType};
use facet_reflect::Partial;

use crate::error::DomDeserializeError;
use crate::naming::to_element_name;
use crate::trace;
use crate::{AttributeRecord, DomEvent, DomParser, DomParserExt};

mod entrypoints;
mod field_map;
mod struct_deser;

use struct_deser::StructDeserializer;

/// Extension trait for chaining deserialization on `Partial`.
pub(crate) trait PartialDeserializeExt<'de, const BORROW: bool, P: DomParser<'de>> {
    /// Deserialize into this partial using the given deserializer.
    fn deserialize_with(
        self,
        deserializer: &mut DomDeserializer<'de, BORROW, P>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>>;

    /// Deserialize into this partial with an explicit expected element name.
    fn deserialize_with_name(
        self,
        deserializer: &mut DomDeserializer<'de, BORROW, P>,
        expected_name: Cow<'static, str>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>>;
}

impl<'de, const BORROW: bool, P: DomParser<'de>> PartialDeserializeExt<'de, BORROW, P>
    for Partial<'de, BORROW>
{
    fn deserialize_with(
        self,
        deserializer: &mut DomDeserializer<'de, BORROW, P>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        deserializer.deserialize_into(self)
    }

    fn deserialize_with_name(
        self,
        deserializer: &mut DomDeserializer<'de, BORROW, P>,
        expected_name: Cow<'static, str>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        deserializer.deserialize_into_named(self, Some(expected_name))
    }
}

/// DOM deserializer.
///
/// The `BORROW` parameter controls whether strings can be borrowed from the input:
/// - `BORROW = true`: Allows zero-copy deserialization of `&str` and `Cow<str>`
/// - `BORROW = false`: All strings are owned, input doesn't need to outlive result
pub struct DomDeserializer<'de, const BORROW: bool, P> {
    parser: P,
    _marker: std::marker::PhantomData<&'de ()>,
}

impl<'de, const BORROW: bool, P> DomDeserializer<'de, BORROW, P>
where
    P: DomParser<'de>,
{
    /// Deserialize a value into an existing Partial.
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** The parser should be positioned such that the next event represents
    /// the value to deserialize. For structs/enums, this means a `NodeStart` is next
    /// (peeked but not consumed). For scalars within an element, the parser should be
    /// inside the element (after `ChildrenStart`).
    ///
    /// **Exit:** The parser will have consumed all events related to this value,
    /// including the closing `NodeEnd` for struct types.
    pub fn deserialize_into(
        &mut self,
        wip: Partial<'de, BORROW>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        self.deserialize_into_named(wip, None)
    }

    /// Deserialize a value into an existing Partial with an optional expected element name.
    ///
    /// When `expected_name` is `Some`, it overrides the element name that would normally
    /// be computed from the type. This is used when deserializing struct fields, where
    /// the XML element name comes from the field name rather than the type name.
    ///
    /// When `expected_name` is `None`, the element name is computed from the type's
    /// `#[facet(rename = "...")]` attribute or its type identifier.
    pub(crate) fn deserialize_into_named(
        &mut self,
        wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        let format_ns = self.parser.format_namespace();

        // Check for field-level proxy first (e.g., #[facet(xml::proxy = ProxyType)] on a field)
        // This takes precedence over container-level proxies.
        if let Some(field) = wip.parent_field()
            && field.effective_proxy(format_ns).is_some()
        {
            let proxy_wip = wip
                .begin_custom_deserialization_with_format(format_ns)
                .map_err(DomDeserializeError::Reflect)?;
            // Deserialize into proxy buffer with the same expected_name
            let proxy_wip = self.deserialize_into_inner(proxy_wip, expected_name)?;
            // Convert proxy -> target via TryFrom
            return proxy_wip.end().map_err(DomDeserializeError::Reflect);
        }

        // Check for container-level proxy (e.g., #[facet(xml::proxy = ProxyType)] on the type)
        // If present, we deserialize into the proxy type, then convert via TryFrom.
        // The expected_name is preserved - it controls the XML element name, not the type.
        if wip.shape().effective_proxy(format_ns).is_some() {
            let (proxy_wip, found) = wip
                .begin_custom_deserialization_from_shape_with_format(format_ns)
                .map_err(DomDeserializeError::Reflect)?;

            if found {
                // Deserialize into proxy buffer with the same expected_name
                let proxy_wip = self.deserialize_into_inner(proxy_wip, expected_name)?;
                // Convert proxy -> target via TryFrom
                return proxy_wip.end().map_err(DomDeserializeError::Reflect);
            }
            // Proxy check returned true but begin_custom_deserialization didn't find it
            // (shouldn't happen, but fall through to normal path)
            return self.deserialize_into_inner(proxy_wip, expected_name);
        }

        self.deserialize_into_inner(wip, expected_name)
    }

    /// Inner deserialization logic, called after proxy handling.
    fn deserialize_into_inner(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        let shape = wip.shape();
        #[cfg(any(test, feature = "tracing"))]
        {
            use owo_colors::OwoColorize;
            let module_path = shape.module_path.unwrap_or("?");
            let module = module_path.dimmed();
            let name = shape.cyan();
            trace!(into = %format_args!("{module}::{name}"));
        }

        // Check for RawMarkup BEFORE transparent wrapper handling
        // (RawMarkup has inner=String but needs special raw capture handling)
        if crate::raw_markup::is_raw_markup(shape) {
            return self.deserialize_raw_markup(wip);
        }

        // Handle transparent wrappers (like NonZero, newtype structs with #[facet(transparent)])
        // Collections (List/Map/Set/Array), Option, and Pointer have .inner for variance but shouldn't use this path
        if shape.inner.is_some()
            && !matches!(
                &shape.def,
                Def::List(_)
                    | Def::Map(_)
                    | Def::Set(_)
                    | Def::Array(_)
                    | Def::Option(_)
                    | Def::Pointer(_)
            )
        {
            wip = wip.begin_inner().map_err(DomDeserializeError::Reflect)?;
            wip = self.deserialize_into_named(wip, expected_name)?;
            wip = wip.end().map_err(DomDeserializeError::Reflect)?;
            return Ok(wip);
        }

        // Check Def::Option first since Option now reports as UserType::Enum
        if let Def::Option(_) = &shape.def {
            return self.deserialize_option(wip, expected_name);
        }

        match &shape.ty {
            Type::User(UserType::Struct(_)) => self.deserialize_struct(wip, expected_name),
            Type::User(UserType::Enum(_)) => self.deserialize_enum(wip, expected_name),
            _ => match &shape.def {
                Def::Scalar => self.deserialize_scalar(wip),
                Def::Pointer(_) => self.deserialize_pointer(wip, expected_name),
                Def::List(_) => self.deserialize_list(wip, expected_name),
                Def::Set(_) => self.deserialize_set(wip, expected_name),
                Def::Map(_) => self.deserialize_map(wip),
                _ => Err(DomDeserializeError::Unsupported(format!(
                    "unsupported type: {:?}",
                    shape.ty
                ))),
            },
        }
    }

    /// Deserialize a struct type.
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned before the struct's `NodeStart` (peeked, not consumed).
    ///
    /// **Exit:** Parser has consumed through the struct's closing `NodeEnd`.
    ///
    /// If `expected_name` is `Some`, it overrides the element name (used when deserializing
    /// a field where the element name comes from the field, not the type).
    fn deserialize_struct(
        &mut self,
        wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        let shape = wip.shape();
        let struct_def = match &shape.ty {
            Type::User(UserType::Struct(def)) => def,
            _ => {
                return Err(DomDeserializeError::Unsupported(
                    "expected struct type".into(),
                ));
            }
        };

        // Use provided expected_name, or compute from shape:
        // rename > rename_all(type_identifier) > lowerCamelCase(type_identifier)
        let expected_name = expected_name.unwrap_or_else(|| {
            if let Some(rename) = shape.get_builtin_attr_value::<&str>("rename") {
                Cow::Borrowed(rename)
            } else if let Some(rename_all) = shape.get_builtin_attr_value::<&str>("rename_all") {
                Cow::Owned(crate::naming::apply_rename_all(
                    shape.type_identifier,
                    rename_all,
                ))
            } else {
                to_element_name(shape.type_identifier)
            }
        });

        // For regular structs, rename_all is handled by facet-derive setting field.rename
        // So we pass None here - the field map will use field.rename if present
        self.deserialize_struct_innards(wip, struct_def, expected_name, None)
    }

    /// Deserialize the innards of a struct-like thing (struct, tuple, or enum variant data).
    ///
    /// Delegates to `StructDeserializer` for the actual implementation.
    ///
    /// The `rename_all` parameter, when provided, overrides any `rename_all` on the struct's shape.
    /// This is used when deserializing enum variants, where the parent enum's `rename_all` should
    /// apply to the variant's fields.
    fn deserialize_struct_innards(
        &mut self,
        wip: Partial<'de, BORROW>,
        struct_def: &'static facet_core::StructType,
        expected_name: Cow<'static, str>,
        rename_all: Option<&'static str>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        // Extract xml::ns_all attribute from the shape
        let ns_all = wip
            .shape()
            .attributes
            .iter()
            .find(|attr| attr.ns == Some("xml") && attr.key == "ns_all")
            .and_then(|attr| attr.get_as::<&str>().copied());

        // Check if deny_unknown_fields is set
        let deny_unknown_fields = wip.shape().has_deny_unknown_fields_attr();

        StructDeserializer::new(
            self,
            struct_def,
            ns_all,
            rename_all,
            expected_name,
            deny_unknown_fields,
        )
        .deserialize(wip)
    }

    /// Deserialize an enum type.
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned at either:
    /// - A `NodeStart` event (element-based variant), or
    /// - A `Text` event (text-based variant, e.g., for enums with a `#[xml::text]` variant)
    ///
    /// **Exit:** All events for this enum have been consumed:
    /// - If entry was `NodeStart`: through the closing `NodeEnd`
    /// - If entry was `Text`: just that text event
    ///
    /// # Variant Selection
    ///
    /// For `NodeStart`: The element tag name is matched against variant names (considering
    /// `#[rename]` attributes). If no match, looks for a variant with `#[xml::custom_element]`.
    ///
    /// For `Text`: Looks for a variant with `#[xml::text]` attribute.
    ///
    /// If `expected_name` is `Some`, it's used for untagged enums. For tagged enums,
    /// the element name is determined by the variant.
    fn deserialize_enum(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        let event = self.parser.peek_event_or_eof("NodeStart or Text")?;

        match event {
            DomEvent::NodeStart { tag, .. } => {
                let tag = tag.clone();
                let enum_shape = wip.shape();
                let enum_def = match &enum_shape.ty {
                    Type::User(UserType::Enum(def)) => def,
                    _ => {
                        return Err(DomDeserializeError::Unsupported(
                            "expected enum type".into(),
                        ));
                    }
                };

                // Extract rename_all from the enum shape BEFORE selecting variant
                // (wip.shape() changes after select_nth_variant)
                // This propagates the enum's rename_all to variant field names
                let rename_all = enum_shape.get_builtin_attr_value::<&str>("rename_all");

                // For untagged enums, the element tag is the enum's name (not a variant name)
                // We need to select the first variant and deserialize the content into it
                let is_untagged = enum_shape.is_untagged();

                let variant_idx = if is_untagged {
                    // For untagged enums, select the first (and typically only) variant
                    // The element tag should match the enum's rename, not a variant name
                    trace!(tag = %tag, "untagged enum - selecting first variant");
                    0
                } else {
                    // For tagged enums, match the element tag against variant names.
                    // Compute effective element name: use rename attribute if present,
                    // otherwise convert to lowerCamelCase.
                    enum_def
                        .variants
                        .iter()
                        .position(|v| {
                            let effective_name: Cow<'_, str> = if v.rename.is_some() {
                                Cow::Borrowed(v.effective_name())
                            } else {
                                to_element_name(v.name)
                            };
                            effective_name == tag
                        })
                        .or_else(|| enum_def.variants.iter().position(|v| v.is_custom_element()))
                        .ok_or_else(|| DomDeserializeError::UnknownElement {
                            tag: tag.to_string(),
                        })?
                };

                let variant = &enum_def.variants[variant_idx];
                wip = wip.select_nth_variant(variant_idx)?;
                trace!(variant_name = variant.name, variant_kind = ?variant.data.kind, is_untagged, "selected variant");

                // Compute element name for this variant
                let variant_element_name: Cow<'static, str> = if is_untagged {
                    // For untagged enums, use provided expected_name or compute from enum type
                    expected_name.clone().unwrap_or_else(|| {
                        let shape = wip.shape();
                        if let Some(renamed) = shape.get_builtin_attr_value::<&str>("rename") {
                            Cow::Borrowed(renamed)
                        } else {
                            to_element_name(shape.type_identifier)
                        }
                    })
                } else if variant.rename.is_some() {
                    Cow::Borrowed(variant.effective_name())
                } else {
                    to_element_name(variant.name)
                };

                // Handle variant based on its kind
                match variant.data.kind {
                    StructKind::Unit => {
                        // Unit variant: just consume the element
                        self.parser.expect_node_start()?;
                        // Skip to end of element
                        let event = self.parser.peek_event_or_eof("ChildrenStart or NodeEnd")?;
                        if matches!(event, DomEvent::ChildrenStart) {
                            self.parser.expect_children_start()?;
                            self.parser.expect_children_end()?;
                        }
                        self.parser.expect_node_end()?;
                    }
                    StructKind::TupleStruct if variant.data.fields.len() == 1 => {
                        // Newtype variant (single unnamed field): deserialize the inner type
                        // Use deserialize_into_named to handle proxies and pass through element name
                        wip = wip
                            .begin_nth_field(0)?
                            .deserialize_with_name(self, variant_element_name)?
                            .end()?;
                    }
                    StructKind::TupleStruct | StructKind::Struct | StructKind::Tuple => {
                        // Struct variant, tuple variant (2+ fields), or tuple type:
                        // deserialize using the variant's data as a StructType
                        // Pass enum's rename_all to apply to variant field names
                        wip = self.deserialize_struct_innards(
                            wip,
                            &variant.data,
                            variant_element_name,
                            rename_all,
                        )?;
                    }
                }
            }
            DomEvent::Text(_) => {
                let text = self.parser.expect_text()?;
                wip = self.deserialize_text_into_enum(wip, text)?;
            }
            other => {
                return Err(DomDeserializeError::TypeMismatch {
                    expected: "NodeStart or Text",
                    got: format!("{other:?}"),
                });
            }
        }

        Ok(wip)
    }

    /// Deserialize text content into an enum by selecting the `#[xml::text]` variant.
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** The text has already been consumed from the parser (passed as argument).
    ///
    /// **Exit:** No parser state change (text was already consumed).
    ///
    /// # Fallback
    ///
    /// If `wip` is not actually an enum, falls back to `set_string_value`.
    fn deserialize_text_into_enum(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        text: Cow<'de, str>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        let enum_def = match &wip.shape().ty {
            Type::User(UserType::Enum(def)) => def,
            _ => {
                return self.set_string_value(wip, text);
            }
        };

        let text_variant_idx = match enum_def.variants.iter().position(|v| v.is_text()) {
            Some(idx) => idx,
            None => {
                // No text variant - either error (XML) or silently discard (HTML)
                if self.parser.is_lenient() {
                    return Ok(wip);
                } else {
                    return Err(DomDeserializeError::Unsupported(
                        "enum has no Text variant for text content".into(),
                    ));
                }
            }
        };

        let variant = &enum_def.variants[text_variant_idx];
        wip = wip.select_nth_variant(text_variant_idx)?;

        // Handle the variant based on its kind
        match variant.data.kind {
            StructKind::TupleStruct => {
                // Newtype variant like Text(String) - navigate to field 0
                wip = wip.begin_nth_field(0)?;
                wip = self.set_string_value(wip, text)?;
                wip = wip.end()?;
            }
            StructKind::Unit => {
                // Unit variant - nothing to set (unusual for text variant but handle it)
            }
            _ => {
                // For other kinds, try direct set (may fail)
                wip = self.set_string_value(wip, text)?;
            }
        }

        Ok(wip)
    }

    /// Deserialize RawMarkup by capturing raw source from the parser.
    fn deserialize_raw_markup(
        &mut self,
        wip: Partial<'de, BORROW>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        // Must be at a NodeStart
        let event = self.parser.peek_event_or_eof("NodeStart for RawMarkup")?;
        if !matches!(event, DomEvent::NodeStart { .. }) {
            return Err(DomDeserializeError::TypeMismatch {
                expected: "NodeStart for RawMarkup",
                got: format!("{event:?}"),
            });
        }

        // Consume the NodeStart
        self.parser
            .next_event()
            .map_err(DomDeserializeError::Parser)?;

        // Try to capture raw - if not supported, fall back to error
        let raw = self
            .parser
            .capture_raw_node()
            .map_err(DomDeserializeError::Parser)?
            .ok_or_else(|| {
                DomDeserializeError::Unsupported("parser does not support raw capture".into())
            })?;

        // Set via the vtable's parse function
        self.set_string_value(wip, raw)
    }

    /// Deserialize a scalar value (string, number, bool, etc.).
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned at either:
    /// - A `Text` event (inline text content), or
    /// - A `NodeStart` event (element wrapping the text content)
    ///
    /// **Exit:** All events for this scalar have been consumed:
    /// - If entry was `Text`: just that text event
    /// - If entry was `NodeStart`: through the closing `NodeEnd`
    ///
    /// # XML Data Model
    ///
    /// In XML, scalars can appear as:
    /// - Attribute values (handled elsewhere)
    /// - Text content: `<parent>text here</parent>`
    /// - Element with text: `<field>value</field>` (element is consumed)
    fn deserialize_scalar(
        &mut self,
        wip: Partial<'de, BORROW>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        trace!("deserialize_scalar called");
        let event = self.parser.peek_event_or_eof("Text or NodeStart")?;
        trace!(event = ?event, "peeked event in deserialize_scalar");
        match event {
            DomEvent::Text(_) => {
                trace!("deserialize_scalar: matched Text arm");
                let text = self.parser.expect_text()?;
                // Use set_string_value_with_proxy for format-specific proxy support
                self.set_string_value_with_proxy(wip, text)
            }
            DomEvent::NodeStart { .. } => {
                trace!("deserialize_scalar: matched NodeStart arm");
                let _tag = self.parser.expect_node_start()?;
                trace!(tag = %_tag, "deserialize_scalar: consumed NodeStart");

                loop {
                    let event = self
                        .parser
                        .peek_event_or_eof("Attribute or ChildrenStart or NodeEnd")?;
                    trace!(event = ?event, "deserialize_scalar: in attr loop");
                    match event {
                        DomEvent::Attribute { .. } => {
                            let AttributeRecord {
                                name: _name,
                                value: _value,
                                namespace: _namespace,
                            } = self.parser.expect_attribute()?;
                            trace!(name = %_name, "deserialize_scalar: consumed Attribute");
                        }
                        DomEvent::ChildrenStart => {
                            self.parser.expect_children_start()?;
                            trace!("deserialize_scalar: consumed ChildrenStart");
                            break;
                        }
                        DomEvent::NodeEnd => {
                            self.parser.expect_node_end()?;
                            trace!("deserialize_scalar: void element, returning empty string");
                            // Use set_string_value_with_proxy for format-specific proxy support
                            return self.set_string_value_with_proxy(wip, Cow::Borrowed(""));
                        }
                        other => {
                            trace!(other = ?other, "deserialize_scalar: unexpected event in attr loop");
                            return Err(DomDeserializeError::TypeMismatch {
                                expected: "Attribute or ChildrenStart or NodeEnd",
                                got: format!("{other:?}"),
                            });
                        }
                    }
                }

                trace!("deserialize_scalar: starting text content loop");
                let mut text_content = String::new();
                loop {
                    let event = self.parser.peek_event_or_eof("Text or ChildrenEnd")?;
                    trace!(event = ?event, "deserialize_scalar: in text content loop");
                    match event {
                        DomEvent::Text(_) => {
                            let text = self.parser.expect_text()?;
                            trace!(text = %text, "deserialize_scalar: got text");
                            text_content.push_str(&text);
                        }
                        DomEvent::ChildrenEnd => {
                            trace!("deserialize_scalar: got ChildrenEnd, breaking text loop");
                            break;
                        }
                        DomEvent::NodeStart { .. } => {
                            trace!("deserialize_scalar: skipping nested NodeStart");
                            self.parser
                                .skip_node()
                                .map_err(DomDeserializeError::Parser)?;
                        }
                        DomEvent::Comment(_) => {
                            let _comment = self.parser.expect_comment()?;
                        }
                        other => {
                            return Err(DomDeserializeError::TypeMismatch {
                                expected: "Text or ChildrenEnd",
                                got: format!("{other:?}"),
                            });
                        }
                    }
                }

                trace!("deserialize_scalar: consuming ChildrenEnd");
                self.parser.expect_children_end()?;
                trace!("deserialize_scalar: consuming NodeEnd");
                self.parser.expect_node_end()?;
                trace!(text_content = %text_content, "deserialize_scalar: setting string value");

                // Use set_string_value_with_proxy for format-specific proxy support
                self.set_string_value_with_proxy(wip, Cow::Owned(text_content))
            }
            other => Err(DomDeserializeError::TypeMismatch {
                expected: "Text or NodeStart",
                got: format!("{other:?}"),
            }),
        }
    }

    /// Deserialize a list (Vec, slice, etc.) from repeated child elements.
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned inside an element, after `ChildrenStart`.
    /// Child elements will be deserialized as list items.
    ///
    /// **Exit:** Parser is positioned at `ChildrenEnd` (peeked, not consumed).
    /// The caller is responsible for consuming `ChildrenEnd` and `NodeEnd`.
    ///
    /// # Note
    ///
    /// This is used for "wrapped" list semantics where a parent element contains
    /// the list items. For "flat" list semantics (items directly as siblings),
    /// see the flat sequence handling in `deserialize_struct_innards`.
    ///
    /// If `expected_name` is provided, it's used as the element name for each item.
    fn deserialize_list(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        wip = wip.init_list()?;

        loop {
            let event = self.parser.peek_event_or_eof("child or ChildrenEnd")?;
            if matches!(event, DomEvent::ChildrenEnd) {
                break;
            }

            wip = wip.begin_list_item()?;
            wip = self.deserialize_into_named(wip, expected_name.clone())?;
            wip = wip.end()?;
        }

        Ok(wip)
    }

    /// Deserialize a set type (HashSet, BTreeSet, etc.).
    ///
    /// Works the same as lists: each child element becomes a set item.
    ///
    /// If `expected_name` is provided, it's used as the element name for each item.
    fn deserialize_set(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        wip = wip.init_set()?;

        loop {
            let event = self.parser.peek_event_or_eof("child or ChildrenEnd")?;
            if matches!(event, DomEvent::ChildrenEnd) {
                break;
            }

            wip = wip.begin_set_item()?;
            wip = self.deserialize_into_named(wip, expected_name.clone())?;
            wip = wip.end()?;
        }

        Ok(wip)
    }

    /// Deserialize a map type (HashMap, BTreeMap, etc.).
    ///
    /// In XML, maps use a **wrapped** model:
    /// - The field name becomes a wrapper element
    /// - Each child element becomes a map entry (tag = key, content = value)
    ///
    /// Example: `<data><alpha>1</alpha><beta>2</beta></data>` -> {"alpha": 1, "beta": 2}
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned at the wrapper element's `NodeStart`.
    ///
    /// **Exit:** Parser has consumed through the wrapper element's `NodeEnd`.
    fn deserialize_map(
        &mut self,
        mut wip: Partial<'de, BORROW>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        // Consume the wrapper element's NodeStart
        let event = self.parser.peek_event_or_eof("NodeStart for map wrapper")?;
        match event {
            DomEvent::NodeStart { .. } => {
                trace!("map wrapper element");
                let _ = self.parser.expect_node_start()?;
            }
            other => {
                return Err(DomDeserializeError::TypeMismatch {
                    expected: "NodeStart for map wrapper",
                    got: format!("{other:?}"),
                });
            }
        }

        // Skip attributes on the wrapper element
        loop {
            let event = self
                .parser
                .peek_event_or_eof("Attribute or ChildrenStart or NodeEnd")?;
            match event {
                DomEvent::Attribute { .. } => {
                    self.parser.expect_attribute()?;
                }
                DomEvent::ChildrenStart => {
                    self.parser.expect_children_start()?;
                    break;
                }
                DomEvent::NodeEnd => {
                    // Empty map (void element)
                    self.parser.expect_node_end()?;
                    return Ok(wip.init_map()?);
                }
                other => {
                    return Err(DomDeserializeError::TypeMismatch {
                        expected: "Attribute or ChildrenStart or NodeEnd",
                        got: format!("{other:?}"),
                    });
                }
            }
        }

        wip = wip.init_map()?;

        // Now parse map entries from children
        loop {
            let event = self.parser.peek_event_or_eof("child or ChildrenEnd")?;
            match event {
                DomEvent::ChildrenEnd => break,
                DomEvent::NodeStart { tag, .. } => {
                    let key = tag.clone();
                    trace!(key = %key, "map entry");

                    // Set the key (element name)
                    wip = wip.begin_key()?;
                    wip = self.set_string_value(wip, key)?;
                    wip = wip.end()?;

                    // Deserialize the value (element content)
                    wip = wip.begin_value()?.deserialize_with(self)?.end()?;
                }
                DomEvent::Text(_) | DomEvent::Comment(_) => {
                    // Skip whitespace text and comments between map entries
                    if matches!(event, DomEvent::Text(_)) {
                        self.parser.expect_text()?;
                    } else {
                        self.parser.expect_comment()?;
                    }
                }
                _ => {
                    return Err(DomDeserializeError::TypeMismatch {
                        expected: "map entry element",
                        got: format!("{event:?}"),
                    });
                }
            }
        }

        // Consume wrapper's ChildrenEnd and NodeEnd
        self.parser.expect_children_end()?;
        self.parser.expect_node_end()?;

        Ok(wip)
    }

    /// Deserialize an Option type.
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned where the optional value would be.
    ///
    /// **Exit:** If value was present, all events for the value have been consumed.
    /// If value was absent, no events consumed.
    ///
    /// # None Detection
    ///
    /// The option is `None` if the next event is `ChildrenEnd` or `NodeEnd`
    /// (indicating no content). Otherwise, the inner value is deserialized.
    ///
    /// If `expected_name` is provided, it's passed through to the inner deserialization.
    fn deserialize_option(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        let event = self.parser.peek_event_or_eof("value")?;
        if matches!(event, DomEvent::ChildrenEnd | DomEvent::NodeEnd) {
            wip = wip.set_default()?;
        } else {
            wip = wip.begin_some()?;
            wip = self.deserialize_into_named(wip, expected_name)?;
            wip = wip.end()?;
        }
        Ok(wip)
    }

    /// Deserialize a pointer type (Box, Arc, Rc, etc.).
    ///
    /// # Parser State Contract
    ///
    /// **Entry:** Parser is positioned at the value that the pointer will wrap.
    ///
    /// **Exit:** All events for the inner value have been consumed.
    ///
    /// # Pointer Actions
    ///
    /// Uses `facet_dessert::begin_pointer` to determine how to handle the pointer:
    /// - `HandleAsScalar`: Treat as scalar (e.g., `Box<str>`)
    /// - `SliceBuilder`: Build a slice (e.g., `Arc<[T]>`)
    /// - `SizedPointee`: Regular pointer to sized type
    ///
    /// If `expected_name` is provided, it's passed through to the inner deserialization.
    fn deserialize_pointer(
        &mut self,
        wip: Partial<'de, BORROW>,
        expected_name: Option<Cow<'static, str>>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        use facet_dessert::{PointerAction, begin_pointer};

        let (wip, action) = begin_pointer(wip)?;

        match action {
            PointerAction::HandleAsScalar => self.deserialize_scalar(wip),
            PointerAction::SliceBuilder => Ok(self.deserialize_list(wip, expected_name)?.end()?),
            PointerAction::SizedPointee => {
                Ok(self.deserialize_into_named(wip, expected_name)?.end()?)
            }
            _ => Err(DomDeserializeError::Unsupported(
                "unsupported pointer action".into(),
            )),
        }
    }

    /// Set a string value on the current partial, parsing it to the appropriate type.
    ///
    /// # Parser State Contract
    ///
    /// **Entry/Exit:** No parser state change. The string value is passed as an argument.
    ///
    /// # Type Handling
    ///
    /// For enums, matches the string against variant names using lowerCamelCase conversion
    /// (matching the serialization behavior). For other types, delegates to
    /// `facet_dessert::set_string_value` which handles parsing the string into the
    /// appropriate scalar type (String, &str, integers, floats, bools, etc.).
    pub(crate) fn set_string_value(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        value: Cow<'de, str>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        // Handle enums specially - match variant names with lowerCamelCase conversion
        // Skip Option (now reports as UserType::Enum) - facet_dessert handles it
        if let Type::User(UserType::Enum(enum_def)) = &wip.shape().ty
            && !matches!(wip.shape().def, Def::Option(_))
        {
            // Find matching variant
            for (idx, variant) in enum_def.variants.iter().enumerate() {
                // Only unit variants can be deserialized from a plain string
                if variant.data.kind != StructKind::Unit {
                    continue;
                }

                // Compute the expected string for this variant (same logic as serialization)
                let variant_str: Cow<'_, str> = if variant.rename.is_some() {
                    Cow::Borrowed(variant.effective_name())
                } else {
                    to_element_name(variant.name)
                };

                if value == variant_str {
                    wip = wip.select_nth_variant(idx)?;
                    return Ok(wip);
                }
            }

            // No match found - fall through to facet_dessert which will give a proper error
        }

        Ok(facet_dessert::set_string_value(
            wip,
            value,
            self.parser.current_span(),
        )?)
    }

    /// Set a string value, handling field-level proxy conversion if present.
    ///
    /// If the field has a proxy attribute (e.g., `#[facet(proxy = PointsProxy)]`),
    /// this will:
    /// 1. Begin custom deserialization (push a frame for the proxy type)
    /// 2. Set the string value into the proxy type
    /// 3. End the frame (which converts proxy -> target via TryFrom)
    ///
    /// If no proxy is present, it just calls `set_string_value` directly.
    ///
    /// This method supports format-specific proxies: if the parser returns a format
    /// namespace (e.g., "xml"), fields with `#[facet(xml::proxy = ...)]` will use
    /// that proxy instead of the format-agnostic one.
    pub(crate) fn set_string_value_with_proxy(
        &mut self,
        mut wip: Partial<'de, BORROW>,
        value: Cow<'de, str>,
    ) -> Result<Partial<'de, BORROW>, DomDeserializeError<P::Error>> {
        // Check if the field has a proxy (format-specific or format-agnostic)
        let format_ns = self.parser.format_namespace();
        let field_proxy = wip
            .parent_field()
            .and_then(|f| f.effective_proxy(format_ns));

        if field_proxy.is_some() {
            // Use custom deserialization through the field-level proxy
            // The format-aware version will select the right proxy
            wip = wip.begin_custom_deserialization_with_format(format_ns)?;
            wip = self.set_string_value(wip, value)?;
            wip = wip.end()?;
            Ok(wip)
        } else if wip.shape().effective_proxy(format_ns).is_some() {
            // The target shape has a container-level proxy
            // Use begin_custom_deserialization_from_shape_with_format
            let (new_wip, _) =
                wip.begin_custom_deserialization_from_shape_with_format(format_ns)?;
            wip = new_wip;
            wip = self.set_string_value(wip, value)?;
            wip = wip.end()?;
            Ok(wip)
        } else {
            self.set_string_value(wip, value)
        }
    }
}
