// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

use crate::error::Error;
use crate::resolver::context::{ReadContext, WriteContext};
use crate::resolver::type_resolver::TypeResolver;
use crate::serializer::collection::{read_collection_type_info, write_collection_type_info};
use crate::serializer::skip::skip_any_value;
use crate::serializer::{ForyDefault, Serializer};
use crate::types::{RefMode, TypeId};
use std::mem;

// Unit type () implementation - represents an empty/unit value with no data
impl Serializer for () {
    #[inline(always)]
    fn fory_write_data(&self, _context: &mut WriteContext) -> Result<(), Error> {
        // Unit type has no data to write
        Ok(())
    }

    #[inline(always)]
    fn fory_read_data(_context: &mut ReadContext) -> Result<Self, Error> {
        // Unit type has no data to read
        Ok(())
    }

    #[inline(always)]
    fn fory_reserved_space() -> usize {
        0
    }

    #[inline(always)]
    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
        // Use NONE - unit type has no runtime data, skip can return early
        Ok(TypeId::NONE)
    }

    #[inline(always)]
    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
        // Use NONE - unit type has no runtime data, skip can return early
        Ok(TypeId::NONE)
    }

    #[inline(always)]
    fn fory_static_type_id() -> TypeId {
        // Use NONE - unit type has no runtime data, skip can return early
        TypeId::NONE
    }

    #[inline(always)]
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

impl ForyDefault for () {
    #[inline(always)]
    fn fory_default() -> Self {}
}

/// Helper function to write a tuple element based on its type characteristics.
/// This handles the different serialization strategies for various element types.
#[inline(always)]
fn write_tuple_element<T: Serializer>(elem: &T, context: &mut WriteContext) -> Result<(), Error> {
    if T::fory_is_option() || T::fory_is_shared_ref() || T::fory_static_type_id() == TypeId::UNKNOWN
    {
        // For Option, shared references, or unknown static types, use full write with ref tracking
        let ref_mode = if T::fory_is_shared_ref() {
            RefMode::Tracking
        } else {
            RefMode::NullOnly
        };
        elem.fory_write(context, ref_mode, false, false)
    } else {
        // For concrete types with known static type IDs, directly write data
        elem.fory_write_data(context)
    }
}

/// Helper function to read a tuple element based on its type characteristics.
#[inline(always)]
fn read_tuple_element<T: Serializer + ForyDefault>(
    context: &mut ReadContext,
    _has_generics: bool,
) -> Result<T, Error> {
    if T::fory_is_option() || T::fory_is_shared_ref() || T::fory_static_type_id() == TypeId::UNKNOWN
    {
        // For Option, shared references, or unknown static types, use full read with ref tracking
        let ref_mode = if T::fory_is_shared_ref() {
            RefMode::Tracking
        } else {
            RefMode::NullOnly
        };
        T::fory_read(context, ref_mode, false)
    } else {
        // For concrete types with known static type IDs, directly read data
        T::fory_read_data(context)
    }
}

impl<T0: Serializer + ForyDefault> Serializer for (T0,) {
    #[inline(always)]
    fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
        if !context.is_compatible() && !context.is_xlang() {
            // Non-compatible mode: write elements directly
            write_tuple_element(&self.0, context)?;
        } else {
            // Compatible mode: use collection protocol (heterogeneous)
            context.writer.write_var_uint32(1);
            let header = 0u8; // No IS_SAME_TYPE flag
            context.writer.write_u8(header);
            self.0.fory_write(context, RefMode::NullOnly, true, false)?;
        }
        Ok(())
    }

    #[inline(always)]
    fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
        write_collection_type_info(context, TypeId::LIST as u32)
    }

    #[inline(always)]
    fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
        if !context.is_compatible() && !context.is_xlang() {
            // Non-compatible mode: read elements directly
            let elem0 = read_tuple_element::<T0>(context, false)?;
            Ok((elem0,))
        } else {
            // Compatible mode: read collection protocol (heterogeneous)
            let len = context.reader.read_varuint32()?;
            let _header = context.reader.read_u8()?;

            let elem0 = if len > 0 {
                T0::fory_read(context, RefMode::NullOnly, true)?
            } else {
                T0::fory_default()
            };

            // Skip any extra elements beyond the first
            for _ in 1..len {
                skip_any_value(context, true)?;
            }

            Ok((elem0,))
        }
    }

    #[inline(always)]
    fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
        read_collection_type_info(context, TypeId::LIST as u32)
    }

    #[inline(always)]
    fn fory_reserved_space() -> usize {
        mem::size_of::<u32>()
    }

    #[inline(always)]
    fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
        Ok(TypeId::LIST)
    }

    #[inline(always)]
    fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
        Ok(TypeId::LIST)
    }

    #[inline(always)]
    fn fory_static_type_id() -> TypeId {
        TypeId::LIST
    }

    #[inline(always)]
    fn fory_is_wrapper_type() -> bool
    where
        Self: Sized,
    {
        true
    }

    #[inline(always)]
    fn as_any(&self) -> &dyn std::any::Any {
        self
    }
}

impl<T0: ForyDefault> ForyDefault for (T0,) {
    #[inline(always)]
    fn fory_default() -> Self {
        (T0::fory_default(),)
    }
}

macro_rules! fory_tuple_field {
    ($tuple:expr, T0) => {
        $tuple.0
    };
    ($tuple:expr, T1) => {
        $tuple.1
    };
    ($tuple:expr, T2) => {
        $tuple.2
    };
    ($tuple:expr, T3) => {
        $tuple.3
    };
    ($tuple:expr, T4) => {
        $tuple.4
    };
    ($tuple:expr, T5) => {
        $tuple.5
    };
    ($tuple:expr, T6) => {
        $tuple.6
    };
    ($tuple:expr, T7) => {
        $tuple.7
    };
    ($tuple:expr, T8) => {
        $tuple.8
    };
    ($tuple:expr, T9) => {
        $tuple.9
    };
    ($tuple:expr, T10) => {
        $tuple.10
    };
    ($tuple:expr, T11) => {
        $tuple.11
    };
    ($tuple:expr, T12) => {
        $tuple.12
    };
    ($tuple:expr, T13) => {
        $tuple.13
    };
    ($tuple:expr, T14) => {
        $tuple.14
    };
    ($tuple:expr, T15) => {
        $tuple.15
    };
    ($tuple:expr, T16) => {
        $tuple.16
    };
    ($tuple:expr, T17) => {
        $tuple.17
    };
    ($tuple:expr, T18) => {
        $tuple.18
    };
    ($tuple:expr, T19) => {
        $tuple.19
    };
    ($tuple:expr, T20) => {
        $tuple.20
    };
    ($tuple:expr, T21) => {
        $tuple.21
    };
    ($tuple:expr, T22) => {
        $tuple.22
    };
    ($tuple:expr, T23) => {
        $tuple.23
    };
    ($tuple:expr, T24) => {
        $tuple.24
    };
    ($tuple:expr, T25) => {
        $tuple.25
    };
    ($tuple:expr, T26) => {
        $tuple.26
    };
    ($tuple:expr, T27) => {
        $tuple.27
    };
    ($tuple:expr, T28) => {
        $tuple.28
    };
    ($tuple:expr, T29) => {
        $tuple.29
    };
    ($tuple:expr, T30) => {
        $tuple.30
    };
    ($tuple:expr, T31) => {
        $tuple.31
    };
    ($tuple:expr, T32) => {
        $tuple.32
    };
    ($tuple:expr, T33) => {
        $tuple.33
    };
    ($tuple:expr, T34) => {
        $tuple.34
    };
    ($tuple:expr, T35) => {
        $tuple.35
    };
    ($tuple:expr, T36) => {
        $tuple.36
    };
    ($tuple:expr, T37) => {
        $tuple.37
    };
    ($tuple:expr, T38) => {
        $tuple.38
    };
    ($tuple:expr, T39) => {
        $tuple.39
    };
    ($tuple:expr, T40) => {
        $tuple.40
    };
}

macro_rules! fory_tuple_count {
    ($($name:ident),+ $(,)?) => {
        0usize $(+ fory_tuple_count!(@one $name))*
    };
    (@one $name:ident) => { 1usize };
}

/// Macro to implement Serializer for tuples of various sizes.
/// Fory supports tuples up to 22 elements, longer tuples are not allowed.
///
/// This handles two serialization modes:
/// 1. Non-compatible mode: Write elements one by one without collection headers and type metadata
/// 2. Compatible mode: Use full collection protocol with headers and type info (always heterogeneous)
#[macro_export]
macro_rules! impl_tuple_serializer {
    // Multiple element tuples (2+)
    ($T0:ident $(, $T:ident)+ $(,)?) => {
        impl<$T0: Serializer + ForyDefault, $($T: Serializer + ForyDefault),*> Serializer for ($T0, $($T),*) {
            #[inline(always)]
            fn fory_write_data(&self, context: &mut WriteContext) -> Result<(), Error> {
                if !context.is_compatible() && !context.is_xlang() {
                    // Non-compatible mode: write elements directly one by one
                    write_tuple_element(&self.0, context)?;
                    $(
                        write_tuple_element(&fory_tuple_field!(self, $T), context)?;
                    )*
                } else {
                    // Compatible mode: use collection protocol (always heterogeneous)
                    let len = fory_tuple_count!($T0, $($T),*);
                    context.writer.write_var_uint32(len as u32);

                    // Write header without IS_SAME_TYPE flag
                    let header = 0u8;
                    context.writer.write_u8(header);

                    // Write each element with its type info
                    self.0.fory_write(context, RefMode::NullOnly, true, false)?;
                    $(
                        fory_tuple_field!(self, $T).fory_write(context, RefMode::NullOnly, true, false)?;
                    )*
                }
                Ok(())
            }

            #[inline(always)]
            fn fory_write_type_info(context: &mut WriteContext) -> Result<(), Error> {
                write_collection_type_info(context, TypeId::LIST as u32)
            }

            #[inline(always)]
            fn fory_read_data(context: &mut ReadContext) -> Result<Self, Error> {
                if !context.is_compatible() && !context.is_xlang() {
                    // Non-compatible mode: read elements directly
                    let elem0 = read_tuple_element::<$T0>(context, false)?;
                    $(
                        #[allow(non_snake_case)]
                        let $T = read_tuple_element::<$T>(context, false)?;
                    )*
                    Ok((elem0, $($T),*))
                } else {
                    // Compatible mode: read collection protocol (always heterogeneous)
                    // Handle flexible length: use defaults for missing elements, skip extras
                    let len = context.reader.read_varuint32()?;
                    let _header = context.reader.read_u8()?;

                    // Track how many elements we've read
                    let mut index = 0u32;

                    // Read first element or use default
                    let elem0 = if index < len {
                        index += 1;
                        $T0::fory_read(context, RefMode::NullOnly, true)?
                    } else {
                        $T0::fory_default()
                    };

                    // Read remaining elements or use defaults
                    $(
                        #[allow(non_snake_case)]
                        let $T = if index < len {
                            index += 1;
                            $T::fory_read(context, RefMode::NullOnly, true)?
                        } else {
                            $T::fory_default()
                        };
                    )*

                    // Skip any extra elements beyond what we expect
                    for _ in index..len {
                        skip_any_value(context, true)?;
                    }

                    Ok((elem0, $($T),*))
                }
            }

            #[inline(always)]
            fn fory_read_type_info(context: &mut ReadContext) -> Result<(), Error> {
                read_collection_type_info(context, TypeId::LIST as u32)
            }

            #[inline(always)]
            fn fory_reserved_space() -> usize {
                mem::size_of::<u32>() // Size for length
            }

            #[inline(always)]
            fn fory_get_type_id(_: &TypeResolver) -> Result<TypeId, Error> {
                Ok(TypeId::LIST)
            }

            #[inline(always)]
            fn fory_type_id_dyn(&self, _: &TypeResolver) -> Result<TypeId, Error> {
                Ok(TypeId::LIST)
            }

            #[inline(always)]
            fn fory_static_type_id() -> TypeId {
                TypeId::LIST
            }

            #[inline(always)]
            fn fory_is_wrapper_type() -> bool
                where
                    Self: Sized, {
                true
            }

            #[inline(always)]
            fn as_any(&self) -> &dyn std::any::Any {
                self
            }
        }

        impl<$T0: ForyDefault, $($T: ForyDefault),*> ForyDefault for ($T0, $($T),*) {
            #[inline(always)]
            fn fory_default() -> Self {
                ($T0::fory_default(), $($T::fory_default()),*)
            }
        }
    };
}

// Implement Serializer for tuples of size 2-22
impl_tuple_serializer!(T0, T1);
impl_tuple_serializer!(T0, T1, T2);
impl_tuple_serializer!(T0, T1, T2, T3);
impl_tuple_serializer!(T0, T1, T2, T3, T4);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15);
impl_tuple_serializer!(T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16);
impl_tuple_serializer!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17
);
impl_tuple_serializer!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18
);
impl_tuple_serializer!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19
);
impl_tuple_serializer!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20
);
impl_tuple_serializer!(
    T0, T1, T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20,
    T21
);
