pub mod internal {

    use std::collections::{HashMap, HashSet};
    use std::sync::Arc;

    use super::super::reflection::{StructDescriptor, StructField, TypeDescriptor};
    use super::super::serializer::{Serializer, TypeAdapter};
    use super::super::serializers::{
        decode_number, encode_uint32, read_u8, skip_value, write_json_escaped_string,
    };
    use super::super::unrecognized::UnrecognizedFields;
    use super::super::unrecognized::{UnrecognizedFieldsData, UnrecognizedFormat};

    // =============================================================================
    // FieldEntry – type-erased per-field adapter
    // =============================================================================

    /// Type-erased interface for a single struct field.
    /// The concrete implementation is [`TypedField`].
    trait FieldEntry<T>: Send + Sync {
        fn entry_name(&self) -> &str;
        fn entry_number(&self) -> i32;
        fn entry_doc(&self) -> &str;
        fn entry_type(&self) -> TypeDescriptor;
        fn is_entry_default(&self, frozen: &T) -> bool;
        fn entry_to_json(&self, frozen: &T, eol_indent: Option<&str>, out: &mut String);
        fn set_entry_from_json(
            &self,
            value: &mut T,
            v: &serde_json::Value,
            keep_unrecognized: bool,
        ) -> Result<(), String>;
        fn encode_entry(&self, frozen: &T, out: &mut Vec<u8>);
        fn decode_entry(&self, value: &mut T, input: &mut &[u8], keep_unrecognized: bool);
    }

    struct TypedField<T: 'static, V: 'static> {
        name: String,
        number: i32,
        doc: String,
        adapter: Serializer<V>,
        getter: fn(&T) -> &V,
        setter: fn(&mut T, V),
    }

    impl<T: 'static, V: 'static> FieldEntry<T> for TypedField<T, V> {
        fn entry_name(&self) -> &str {
            &self.name
        }

        fn entry_number(&self) -> i32 {
            self.number
        }

        fn entry_doc(&self) -> &str {
            &self.doc
        }

        fn entry_type(&self) -> TypeDescriptor {
            self.adapter.adapter().type_descriptor()
        }

        fn is_entry_default(&self, frozen: &T) -> bool {
            self.adapter.adapter().is_default((self.getter)(frozen))
        }

        fn entry_to_json(&self, frozen: &T, eol_indent: Option<&str>, out: &mut String) {
            self.adapter
                .adapter()
                .to_json((self.getter)(frozen), eol_indent, out);
        }

        fn set_entry_from_json(
            &self,
            value: &mut T,
            v: &serde_json::Value,
            keep_unrecognized: bool,
        ) -> Result<(), String> {
            let val = self.adapter.adapter().from_json(v, keep_unrecognized)?;
            (self.setter)(value, val);
            Ok(())
        }

        fn encode_entry(&self, frozen: &T, out: &mut Vec<u8>) {
            self.adapter.adapter().encode((self.getter)(frozen), out);
        }

        fn decode_entry(&self, value: &mut T, input: &mut &[u8], keep_unrecognized: bool) {
            if let Ok(val) = self.adapter.adapter().decode(input, keep_unrecognized) {
                (self.setter)(value, val);
            }
        }
    }

    // =============================================================================
    // StructAdapter
    // =============================================================================

    /// Implements [`TypeAdapter<T>`] for a Skir struct type.
    ///
    /// For use only by code generated by the Skir Rust code generator.
    ///
    /// - `T` is the frozen (immutable) struct type.
    ///
    /// Usage: call [`StructAdapter::new`], then [`StructAdapter::add_field`] /
    /// [`StructAdapter::add_removed_number`] for each field, then
    /// [`StructAdapter::finalize`] to finish.
    pub struct StructAdapter<T: 'static + Default> {
        get_unrecognized: fn(&T) -> &Option<UnrecognizedFields<T>>,
        set_unrecognized: fn(&mut T, Option<Box<UnrecognizedFieldsData<T>>>),
        ordered_entries: Vec<Box<dyn FieldEntry<T>>>,
        /// name → index in ordered_entries (built in finalize after sort)
        name_to_index: HashMap<String, usize>,
        /// slot number → index in ordered_entries; None = removed or absent slot
        slot_to_index: Vec<Option<usize>>,
        removed_numbers: HashSet<i32>,
        max_number: i32,
        /// Pre-allocated descriptor so typeDescriptor() can return a stable Arc
        /// even before finalize is called (needed for recursive field types).
        desc: Arc<StructDescriptor>,
    }

    impl<T: 'static + Default> StructAdapter<T> {
        /// Creates a new `StructAdapter`.
        ///
        /// Call [`add_field`](Self::add_field) / [`add_removed_number`](Self::add_removed_number)
        /// for each field and removed number, then [`finalize`](Self::finalize).
        pub fn new(
            module_path: &str,
            qualified_name: &str,
            doc: &str,
            get_unrecognized: fn(&T) -> &Option<UnrecognizedFields<T>>,
            set_unrecognized: fn(&mut T, Option<Box<UnrecognizedFieldsData<T>>>),
        ) -> Self {
            // Pre-allocate the descriptor with an empty fields set so that
            // typeDescriptor() can return a valid Arc even before finalize.
            // Fields and removed numbers are populated later.
            let desc = Arc::new(StructDescriptor::new(
                module_path.to_string(),
                qualified_name.to_string(),
                doc.to_string(),
            ));
            StructAdapter {
                get_unrecognized,
                set_unrecognized,
                ordered_entries: Vec::new(),
                name_to_index: HashMap::new(),
                slot_to_index: Vec::new(),
                removed_numbers: HashSet::new(),
                max_number: -1,
                desc,
            }
        }

        /// Registers a struct field. Must be called before [`finalize`](Self::finalize).
        pub fn add_field<V: 'static>(
            &mut self,
            name: &str,
            number: i32,
            ser: Serializer<V>,
            doc: &str,
            getter: fn(&T) -> &V,
            setter: fn(&mut T, V),
        ) {
            let idx = self.ordered_entries.len();
            self.name_to_index.insert(name.to_string(), idx);
            self.ordered_entries.push(Box::new(TypedField::<T, V> {
                name: name.to_string(),
                number,
                doc: doc.to_string(),
                adapter: ser,
                getter,
                setter,
            }));
            if number > self.max_number {
                self.max_number = number;
            }
        }

        /// Registers a field number that was removed from the schema.
        /// Must be called before [`finalize`](Self::finalize).
        pub fn add_removed_number(&mut self, number: i32) {
            self.removed_numbers.insert(number);
            if number > self.max_number {
                self.max_number = number;
            }
        }

        pub fn finalize(&mut self) {
            // Sort ordered_entries by field number.
            self.ordered_entries.sort_by_key(|e| e.entry_number());

            // Rebuild name_to_index after sort.
            self.name_to_index.clear();
            for (idx, e) in self.ordered_entries.iter().enumerate() {
                self.name_to_index.insert(e.entry_name().to_string(), idx);
            }

            // Build slot_to_index: indexed by slot number.
            let slot_count = if self.max_number >= 0 {
                (self.max_number + 1) as usize
            } else {
                0
            };
            self.slot_to_index = vec![None; slot_count];
            for (idx, e) in self.ordered_entries.iter().enumerate() {
                self.slot_to_index[e.entry_number() as usize] = Some(idx);
            }

            // Populate StructDescriptor fields and removed numbers.
            let fields: Vec<StructField> = self
                .ordered_entries
                .iter()
                .map(|e| {
                    StructField::new(
                        e.entry_name().to_string(),
                        e.entry_number(),
                        e.entry_type(),
                        e.entry_doc().to_string(),
                    )
                })
                .collect();
            self.desc.set_fields(fields);
            self.desc
                .set_removed_numbers(std::mem::take(&mut self.removed_numbers));
        }

        /// Returns a reference to the pre-allocated [`StructDescriptor`] for this
        /// adapter. Valid even before it is finalized, which is necessary for
        /// recursive struct field types.
        pub fn descriptor(&self) -> Arc<StructDescriptor> {
            Arc::clone(&self.desc)
        }

        // -----------------------------------------------------------------------
        // TypeAdapter implementation helpers
        // -----------------------------------------------------------------------

        fn is_default_impl(&self, input: &T) -> bool {
            if (self.get_unrecognized)(input).is_some() {
                return false;
            }
            self.ordered_entries
                .iter()
                .all(|e| e.is_entry_default(input))
        }

        fn to_json_impl(&self, input: &T, eol_indent: Option<&str>, out: &mut String) {
            if let Some(indent) = eol_indent {
                self.to_readable_json(input, indent, out);
            } else {
                self.to_dense_json(input, out);
            }
        }

        /// Writes `input` as a JSON array (dense format).
        /// Trailing default slots are omitted unless the struct carries unrecognized
        /// JSON fields from a newer schema version.
        fn to_dense_json(&self, input: &T, out: &mut String) {
            let unrecognized = (self.get_unrecognized)(input);
            out.push('[');

            if let Some(u) = unrecognized.as_deref() {
                if u.format == UnrecognizedFormat::DenseJson && !u.values.is_empty() {
                    // Write all recognized slots, then append stored unrecognized elements.
                    let recognized_count = self.slot_to_index.len();
                    for i in 0..recognized_count {
                        if i > 0 {
                            out.push(',');
                        }
                        if let Some(idx) = self.slot_to_index[i] {
                            self.ordered_entries[idx].entry_to_json(input, None, out);
                        } else {
                            out.push('0');
                        }
                    }
                    // Append the stored unrecognized JSON. values is a JSON array string
                    // like "[1,\"foo\"]". We strip the surrounding brackets and append.
                    let extra_json = std::str::from_utf8(&u.values).unwrap_or("[]");
                    // Strip outer '[' and ']', then append each element.
                    let inner = extra_json.trim_matches(|c| c == '[' || c == ']' || c == ' ');
                    if !inner.is_empty() {
                        if recognized_count > 0 || !inner.is_empty() {
                            out.push(',');
                        }
                        out.push_str(inner);
                    }
                    out.push(']');
                    return;
                }
            }

            // No unrecognized JSON fields: omit trailing default slots.
            let slot_count = self.get_slot_count(input);
            for i in 0..slot_count {
                if i > 0 {
                    out.push(',');
                }
                if i < self.slot_to_index.len() {
                    if let Some(idx) = self.slot_to_index[i] {
                        self.ordered_entries[idx].entry_to_json(input, None, out);
                    } else {
                        out.push('0');
                    }
                } else {
                    out.push('0');
                }
            }
            out.push(']');
        }

        /// Writes `input` as a JSON object (readable format), omitting default fields.
        fn to_readable_json(&self, input: &T, eol_indent: &str, out: &mut String) {
            out.push('{');
            let child_indent = format!("{eol_indent}  ");
            let mut first = true;
            for e in &self.ordered_entries {
                if e.is_entry_default(input) {
                    continue;
                }
                if !first {
                    out.push(',');
                }
                first = false;
                out.push_str(&child_indent);
                write_json_escaped_string(e.entry_name(), out);
                out.push_str(": ");
                e.entry_to_json(input, Some(&child_indent), out);
            }
            if !first {
                out.push_str(eol_indent);
            }
            out.push('}');
        }

        /// Returns the number of slots needed to encode `input` (= field number of
        /// the last non-default field + 1, or 0 if all fields are default).
        fn get_slot_count(&self, input: &T) -> usize {
            for e in self.ordered_entries.iter().rev() {
                if !e.is_entry_default(input) {
                    return (e.entry_number() + 1) as usize;
                }
            }
            0
        }

        fn from_json_impl(
            &self,
            v: &serde_json::Value,
            keep_unrecognized: bool,
        ) -> Result<T, String> {
            match v {
                serde_json::Value::Number(_) => {
                    // Dense default: 0 → return default instance.
                    Ok(T::default())
                }
                serde_json::Value::Array(_) => self.from_dense_json(v, keep_unrecognized),
                serde_json::Value::Object(_) => self.from_readable_json(v),
                _ => Ok(T::default()),
            }
        }

        fn from_dense_json(
            &self,
            arr: &serde_json::Value,
            keep_unrecognized: bool,
        ) -> Result<T, String> {
            let items = arr.as_array().unwrap();
            let mut t = T::default();
            let recognized_count = self.slot_to_index.len();
            let total_items = items.len();

            let mut num_slots_to_fill = total_items;
            if total_items > recognized_count {
                // The encoded array has slots from a newer schema version.
                if keep_unrecognized {
                    let extra_items = &items[recognized_count..];
                    // Serialize extra items as a JSON array string to store them.
                    let extra_arr = serde_json::Value::Array(extra_items.to_vec());
                    let json_bytes = serde_json::to_vec(&extra_arr).map_err(|e| e.to_string())?;
                    (self.set_unrecognized)(
                        &mut t,
                        Some(UnrecognizedFieldsData::new_from_json(
                            total_items as u32,
                            json_bytes,
                        )),
                    );
                }
                num_slots_to_fill = recognized_count;
            }

            for e in &self.ordered_entries {
                let n = e.entry_number() as usize;
                if n >= num_slots_to_fill {
                    break;
                }
                e.set_entry_from_json(&mut t, &items[n], keep_unrecognized)?;
            }

            Ok(t)
        }

        fn from_readable_json(&self, obj: &serde_json::Value) -> Result<T, String> {
            let json_obj = obj.as_object().unwrap();
            let mut t = T::default();
            for (key, val) in json_obj {
                if let Some(&idx) = self.name_to_index.get(key.as_str()) {
                    self.ordered_entries[idx].set_entry_from_json(&mut t, val, false)?;
                }
            }
            Ok(t)
        }

        fn encode_impl(&self, input: &T, out: &mut Vec<u8>) {
            let unrecognized = (self.get_unrecognized)(input);
            let (total_slot_count, recognized_slot_count, unrecognized_bytes) =
                if let Some(u) = unrecognized.as_deref() {
                    if u.format == UnrecognizedFormat::Bytes && !u.values.is_empty() {
                        (
                            u.array_len as usize,
                            self.slot_to_index.len(),
                            Some(u.values.as_slice()),
                        )
                    } else {
                        let c = self.get_slot_count(input);
                        (c, c, None)
                    }
                } else {
                    let c = self.get_slot_count(input);
                    (c, c, None)
                };

            if total_slot_count <= 3 {
                out.push((246 + total_slot_count) as u8);
            } else {
                out.push(250);
                encode_uint32(total_slot_count as u32, out);
            }

            for i in 0..recognized_slot_count {
                if i < self.slot_to_index.len() {
                    if let Some(idx) = self.slot_to_index[i] {
                        self.ordered_entries[idx].encode_entry(input, out);
                    } else {
                        out.push(0); // removed or absent slot
                    }
                } else {
                    out.push(0);
                }
            }

            if let Some(ub) = unrecognized_bytes {
                out.extend_from_slice(ub);
            }
        }

        fn decode_impl(&self, input: &mut &[u8], keep_unrecognized: bool) -> Result<T, String> {
            let wire = read_u8(input)?;
            if wire == 0 || wire == 246 {
                return Ok(T::default());
            }

            let mut t = T::default();
            let encoded_slot_count: usize = if wire == 250 {
                decode_number(input)? as usize
            } else {
                (wire as usize).wrapping_sub(246)
            };

            let recognized_count = self.slot_to_index.len();
            let slots_to_fill = encoded_slot_count.min(recognized_count);

            for i in 0..slots_to_fill {
                if let Some(idx) = self.slot_to_index[i] {
                    self.ordered_entries[idx].decode_entry(&mut t, input, keep_unrecognized);
                } else {
                    // Removed field: skip its encoded value.
                    skip_value(input)?;
                }
            }

            if encoded_slot_count > recognized_count {
                // Extra slots from a newer schema version.
                if keep_unrecognized {
                    // Capture the raw wire bytes of the unrecognized slots so they
                    // can be round-tripped back to binary encoding later.
                    let before = *input;
                    let before_len = before.len();
                    for _ in recognized_count..encoded_slot_count {
                        skip_value(input)?;
                    }
                    let bytes_consumed = before_len - input.len();
                    let captured = before[..bytes_consumed].to_vec();
                    (self.set_unrecognized)(
                        &mut t,
                        Some(UnrecognizedFieldsData::new_from_bytes(
                            encoded_slot_count as u32,
                            captured,
                        )),
                    );
                } else {
                    for _ in recognized_count..encoded_slot_count {
                        skip_value(input)?;
                    }
                }
            }

            Ok(t)
        }

        fn type_descriptor_impl(&self) -> TypeDescriptor {
            TypeDescriptor::Struct(Arc::clone(&self.desc))
        }
    }

    impl<T: 'static + Default> TypeAdapter<T> for StructAdapter<T> {
        fn is_default(&self, input: &T) -> bool {
            self.is_default_impl(input)
        }

        fn to_json(&self, input: &T, eol_indent: Option<&str>, out: &mut String) {
            self.to_json_impl(input, eol_indent, out);
        }

        fn from_json(
            &self,
            json: &serde_json::Value,
            keep_unrecognized_values: bool,
        ) -> Result<T, String> {
            self.from_json_impl(json, keep_unrecognized_values)
        }

        fn encode(&self, input: &T, out: &mut Vec<u8>) {
            self.encode_impl(input, out);
        }

        fn decode(&self, input: &mut &[u8], keep_unrecognized_values: bool) -> Result<T, String> {
            self.decode_impl(input, keep_unrecognized_values)
        }

        fn type_descriptor(&self) -> TypeDescriptor {
            self.type_descriptor_impl()
        }

        fn clone_box(&self) -> Box<dyn TypeAdapter<T>> {
            unreachable!("StructAdapter is always accessed through a &'static reference")
        }
    }

    /// Creates a [`Serializer`] backed by the given `'static` [`StructAdapter`]
    /// reference. For use only by generated code.
    pub fn struct_serializer_from_static<T: 'static + Default>(
        adapter: &'static StructAdapter<T>,
    ) -> Serializer<T> {
        Serializer::new_borrowed(adapter)
    }
} // pub mod internal
