use crate::{
    error::Error,
    interpreter::{
        data::{
            ArcisField,
            ArcisValue,
            BoolData,
            CollectionKind,
            Data,
            StructId,
            StructKind,
            ValData,
        },
        data_store::SimpleAddress,
        generic_context::VarId,
        library::packing::packing,
        memory::Memory,
    },
};
use arcis_compiler::{
    core::circuits::boolean::boolean_value::BooleanValue,
    traits::FromLeBits,
    utils::{packing::DataSize, used_field::UsedField},
    ArcisFloatValue,
};
use arcis_interface::{ScalarKind, Value};
use ff::{Field, PrimeField};
use serde::Serialize;
use std::{
    fmt::{Display, Write},
    ops::Deref,
};

/// Metadata about integer types including signedness and bit width.
///
/// Used to properly interpret and validate integer values in the circuit interpreter,
/// ensuring correct handling of signed vs unsigned integers and their bounds.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub struct IntegerInfo {
    /// Whether this is a signed integer type (`true` for i8, i16, i32, i64, i128).
    pub(crate) signed: bool,
    /// Bit width of the integer (8, 16, 32, 64, or 128).
    pub(crate) width: usize,
}

impl IntegerInfo {
    pub const fn new(signed: bool, width: usize) -> Self {
        Self { signed, width }
    }
}

impl Default for IntegerInfo {
    fn default() -> Self {
        IntegerInfo {
            signed: true,
            width: 128,
        }
    }
}

/// Rust type representation for circuit values.
///
/// Maps Rust types to their circuit equivalents, tracking size and signedness
/// information needed for proper value interpretation and bounds checking.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize)]
pub enum RustType {
    /// Integer type with optional size/signedness info.
    /// `None` means the type is not yet fully inferred (e.g., untyped integer literals).
    Integer(Option<IntegerInfo>),
    /// Floating-point type (f64).
    Float,
    /// ArcisField (255 bits).
    ArcisField,
}

impl RustType {
    pub const ARCIS_FIELD: RustType = RustType::ArcisField;

    pub const BASE_FIELD: RustType = RustType::ArcisField;

    pub fn build_from_arcis_value(&self, v: ArcisValue) -> ValData {
        ValData(v, *self)
    }
    /// Caps (bounds-checks) an Arcis value to fit within this type's valid range.
    ///
    /// # Signed Integer Handling
    /// For signed integers, this maps the two's complement range [-2^(n-1), 2^(n-1)-1]
    /// to [0, 2^n-1] by adding an offset, applies unsigned capping, then subtracts the offset.
    ///
    /// # Example
    /// For i8 (width=8):
    /// - Valid range: [-128, 127]
    /// - Add offset 128: [0, 255]
    /// - Cap to 8 bits: ensures value is in [0, 255]
    /// - Subtract offset 128: result is in [-128, 127]
    pub fn cap_arcis_value(&self, v: ArcisValue) -> ArcisValue {
        fn cap_unsigned(v: ArcisValue, size: usize) -> ArcisValue {
            ArcisValue::new(arcis_compiler::core::expressions::field_expr::FieldExpr::Cap(v, size))
        }
        match *self {
            RustType::Integer(None) => {
                // Type inference not complete - no capping applied.
                // This is mostly the case with untyped integer literals like 42.
                // TODO: Maybe we should error out somehow ?
                v
            }
            RustType::Integer(Some(IntegerInfo { signed, width })) => {
                if width < ArcisField::NUM_BITS as usize {
                    if signed {
                        // For signed integers, offset to unsigned range before capping
                        let offset = ArcisField::power_of_two(width - 1);
                        cap_unsigned(v + offset, width) - offset
                    } else {
                        cap_unsigned(v, width)
                    }
                } else {
                    v
                }
            }
            RustType::Float => v,
            RustType::ArcisField => v,
        }
    }
    pub fn is_float(&self) -> bool {
        matches!(self, RustType::Float)
    }
    pub fn intersect(self, other: Self) -> Result<Self, &'static str> {
        use RustType::*;
        let res = match (self, other) {
            (Float, Float) => Float,
            (ArcisField, ArcisField) => ArcisField,
            (Integer(x), Integer(y)) => {
                if x == y {
                    Integer(x)
                } else if x.is_none() {
                    Integer(y)
                } else if y.is_none() {
                    Integer(x)
                } else {
                    return Err("These integers are not compatible.");
                }
            }
            _ => return Err("Attempted to intersect incompatible numerical rust types."),
        };
        Ok(res)
    }
    pub fn usize() -> Self {
        RustType::Integer(Some(IntegerInfo::new(false, usize::BITS as usize)))
    }
    pub fn isize() -> Self {
        RustType::Integer(Some(IntegerInfo::new(false, isize::BITS as usize)))
    }
    pub fn u8() -> Self {
        RustType::Integer(Some(IntegerInfo::new(false, u8::BITS as usize)))
    }
    pub fn integer_from_suffix(suffix: &str) -> Self {
        match suffix {
            "u8" => RustType::Integer(Some(IntegerInfo::new(false, u8::BITS as usize))),
            "u16" => RustType::Integer(Some(IntegerInfo::new(false, u16::BITS as usize))),
            "u32" => RustType::Integer(Some(IntegerInfo::new(false, u32::BITS as usize))),
            "u64" => RustType::Integer(Some(IntegerInfo::new(false, u64::BITS as usize))),
            "u128" => RustType::Integer(Some(IntegerInfo::new(false, u128::BITS as usize))),
            "usize" => RustType::Integer(Some(IntegerInfo::new(false, usize::BITS as usize))),
            "i8" => RustType::Integer(Some(IntegerInfo::new(true, i8::BITS as usize))),
            "i16" => RustType::Integer(Some(IntegerInfo::new(true, i16::BITS as usize))),
            "i32" => RustType::Integer(Some(IntegerInfo::new(true, i32::BITS as usize))),
            "i64" => RustType::Integer(Some(IntegerInfo::new(true, i64::BITS as usize))),
            "i128" => RustType::Integer(Some(IntegerInfo::new(true, i128::BITS as usize))),
            "isize" => RustType::Integer(Some(IntegerInfo::new(true, isize::BITS as usize))),
            _ => RustType::Integer(None),
        }
    }
    pub fn min_and_max(&self) -> (ArcisField, ArcisField) {
        let full_unsigned_bounds = (ArcisField::ZERO, -ArcisField::ONE);
        match self {
            RustType::Integer(None) => full_unsigned_bounds,
            RustType::Integer(Some(v)) => {
                if v.signed {
                    if v.width >= ArcisField::NUM_BITS as usize {
                        (ArcisField::TWO_INV, ArcisField::TWO_INV - ArcisField::ONE)
                    } else {
                        let temp = ArcisField::power_of_two(v.width - 1);
                        (-temp, temp - ArcisField::ONE)
                    }
                } else if v.width >= ArcisField::NUM_BITS as usize {
                    full_unsigned_bounds
                } else {
                    (
                        ArcisField::ZERO,
                        ArcisField::power_of_two(v.width) - ArcisField::ONE,
                    )
                }
            }
            RustType::Float => ArcisFloatValue::max_bounds(),
            RustType::ArcisField => full_unsigned_bounds,
        }
    }
    pub fn n_bools(&self) -> Result<usize, &'static str> {
        match self {
            RustType::Integer(Some(IntegerInfo { width, .. })) => Ok(*width),
            RustType::Integer(None) => {
                Err("Unknown integer types cannot be generated from booleans.")
            }
            RustType::Float => Err("Floats cannot be generated from booleans."),
            RustType::ArcisField => Err("Ciphertexts cannot be generated from booleans."),
        }
    }
    pub fn build_from_bools(&self, rev_values: &mut Vec<BooleanValue>) -> ValData {
        let data = match self {
            RustType::Integer(Some(IntegerInfo { width, signed })) => {
                let mut bits = rev_values.split_off(rev_values.len() - *width);
                bits.reverse();
                ArcisValue::from_le_bits(bits, *signed)
            }
            _ => panic!("Cannot be built from bools."),
        };
        ValData(data, *self)
    }
    pub fn data_size(&self) -> Result<DataSize, &'static str> {
        let size = match self {
            RustType::Integer(Some(IntegerInfo { width, .. })) => {
                if *width == ArcisField::NUM_BITS as usize {
                    DataSize::Full
                } else {
                    let pow = width.checked_ilog2().ok_or("Cannot size empty integers.")? as u8;
                    if *width != 1 << pow {
                        return Err("Cannot have field with non-power-of-two sizes.");
                    }
                    DataSize::PowerOfTwo(pow)
                }
            }
            RustType::Integer(None) => return Err("Unknown data size for integer literal."),
            RustType::Float => DataSize::Full,
            RustType::ArcisField => DataSize::Full,
        };
        Ok(size)
    }
    pub fn is_signed(&self) -> bool {
        // Floats should be considered unsigned here, so that they are put in packs without offset.
        matches!(
            self,
            RustType::Integer(Some(IntegerInfo { signed: true, .. }))
        )
    }
    pub fn can_be_used_as_index(&self) -> bool {
        *self == Self::isize() || self.can_be_used_as_size()
    }
    pub fn can_be_used_as_size(&self) -> bool {
        *self == Self::usize() || *self == RustType::Integer(None)
    }
    pub fn cannot_be_neg(&self) -> bool {
        matches!(
            self,
            RustType::Integer(Some(IntegerInfo { signed: false, .. }))
        )
    }
}

impl std::fmt::Display for RustType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            RustType::Integer(i) => match i {
                None => f.write_str("{integer}"),
                Some(ty) => {
                    let prefix = if ty.signed { 'i' } else { 'u' };
                    f.write_char(prefix)?;
                    write!(f, "{}", ty.width)
                }
            },
            RustType::Float => f.write_str("f64"),
            RustType::ArcisField => f.write_str("ArcisCiphertext"),
        }
    }
}

/// Curve type representation for circuit values.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum CurveType {
    /// Arcis x25519 public key (a curve point).
    X25519Pubkey,
}

impl CurveType {
    pub fn intersect(self, other: Self) -> Result<Self, &'static str> {
        let res = match (self, other) {
            (CurveType::X25519Pubkey, CurveType::X25519Pubkey) => CurveType::X25519Pubkey,
        };
        Ok(res)
    }
}

impl std::fmt::Display for CurveType {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            CurveType::X25519Pubkey => f.write_str("ArcisX25519Pubkey"),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct TypedStruct {
    id: StructId,
    types: Vec<SupportedType>,
}

impl TypedStruct {
    pub fn new(id: StructId, types: Vec<SupportedType>) -> Self {
        Self { id, types }
    }
    pub fn id(&self) -> StructId {
        self.id.clone()
    }
    pub fn types(&self) -> &[SupportedType] {
        &self.types
    }
    pub fn types_mut(&mut self) -> &mut [SupportedType] {
        &mut self.types
    }
}

impl Deref for TypedStruct {
    type Target = StructId;
    fn deref(&self) -> &Self::Target {
        &self.id
    }
}

impl std::fmt::Display for TypedStruct {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_str(self.get_name())
    }
}

#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum ArrayLen {
    Computed(usize),
    ConstVar(VarId),
}

impl ArrayLen {
    pub fn value(&self) -> Result<usize, &'static str> {
        match self {
            ArrayLen::Computed(a) => Ok(*a),
            ArrayLen::ConstVar(_) => Err("Array value is unknown."),
        }
    }
}

impl Display for ArrayLen {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            ArrayLen::Computed(i) => {
                write!(f, "{}", *i)
            }
            ArrayLen::ConstVar(t) => {
                write!(f, "Unknown Generic Value ({:?})", *t)
            }
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash, Default)]
pub enum SliceLen {
    #[default]
    None,
    NArcisValues(Box<SupportedType>),
    NPackedArcisValues(Box<SupportedType>),
}

impl SliceLen {
    pub fn from_ident_and_ty(ident: &syn::Ident, ty: SupportedType) -> Result<Self, Error> {
        if ident == "n_arcis_values" {
            Ok(SliceLen::NArcisValues(Box::new(ty)))
        } else if ident == "n_packed_arcis_values" {
            Ok(SliceLen::NPackedArcisValues(Box::new(ty)))
        } else {
            Err(Error::new_spanned(
                ident,
                format!("Unknown ident {ident} for `slice_len`."),
            ))
        }
    }
    pub fn value(&self) -> Result<usize, &'static str> {
        match self {
            SliceLen::None => Err("SliceLen is None."),
            SliceLen::NArcisValues(ty) => ty.n_arcis_values(),
            SliceLen::NPackedArcisValues(ty) => packing(ty).map(|x| x.1.len()),
        }
    }
}

impl Display for SliceLen {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            SliceLen::None => f.write_str("_"),
            SliceLen::NArcisValues(ty) => write!(f, "({ty})::n_arcis_values()"),
            SliceLen::NPackedArcisValues(ty) => write!(f, "n_packed_arcis_values({ty})"),
        }
    }
}

#[derive(Debug, Clone, Copy)]
pub enum PointerDepth {
    FromNonPtr(usize),
    FromAny(usize),
}

impl PointerDepth {
    pub fn plus_one(&self) -> Self {
        match self {
            PointerDepth::FromNonPtr(u) => PointerDepth::FromNonPtr(*u + 1),
            PointerDepth::FromAny(u) => PointerDepth::FromAny(*u + 1),
        }
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum SupportedType {
    Unit,
    FieldValue(RustType),
    Bool,
    // Could e.g. be a x25519 pubkey, a ed25519 verifying key or part of a ElGamal ciphertext.
    Curve(CurveType),
    Struct(TypedStruct),
    Ref(Box<SupportedType>),
    Array(Box<SupportedType>, ArrayLen),
    Tuple(Vec<SupportedType>),
    Slice(Box<SupportedType>, SliceLen),
    Fn,
    Var(VarId),
    ImplAny,
}
impl SupportedType {
    pub fn pointer_depth(&self) -> PointerDepth {
        match self {
            SupportedType::Unit => PointerDepth::FromNonPtr(0),
            SupportedType::FieldValue(_) => PointerDepth::FromNonPtr(0),
            SupportedType::Bool => PointerDepth::FromNonPtr(0),
            SupportedType::Curve(_) => PointerDepth::FromNonPtr(0),
            SupportedType::Struct(_) => PointerDepth::FromNonPtr(0),
            SupportedType::Ref(f) => f.pointer_depth().plus_one(),
            SupportedType::Array(_, _) => PointerDepth::FromNonPtr(0),
            SupportedType::Tuple(_) => PointerDepth::FromNonPtr(0),
            SupportedType::Slice(_, _) => PointerDepth::FromNonPtr(0),
            SupportedType::Fn => PointerDepth::FromNonPtr(0),
            SupportedType::Var(_) => PointerDepth::FromAny(0),
            SupportedType::ImplAny => PointerDepth::FromAny(0),
        }
    }
    pub fn to_value(&self) -> Result<Value, &'static str> {
        let res = match self {
            SupportedType::Unit => Value::Tuple(Vec::new()),
            SupportedType::FieldValue(r) => match r {
                RustType::Integer(Some(info)) => Value::Scalar {
                    size_in_bits: info.width,
                    kind: if info.signed {
                        ScalarKind::Signed
                    } else {
                        ScalarKind::Unsigned
                    },
                },
                RustType::ArcisField => Value::Ciphertext {
                    size_in_bits: ArcisField::NUM_BITS as usize,
                },
                RustType::Float => Value::Float { size_in_bits: 64 },
                RustType::Integer(None) => {
                    return Err("integer literal");
                }
            },
            SupportedType::Bool => Value::Bool,
            SupportedType::Curve(c) => match c {
                CurveType::X25519Pubkey => Value::ArcisX25519Pubkey,
            },

            SupportedType::Struct(s) => {
                let v: Result<Vec<_>, _> = s.types().iter().map(|ty| ty.to_value()).collect();
                Value::Struct(v?)
            }
            SupportedType::Ref(t) => t.to_value()?,
            SupportedType::Array(t, len) => {
                let val = t.to_value()?;
                Value::Array(vec![val; len.value()?])
            }
            SupportedType::Tuple(t) => {
                let v: Result<Vec<_>, _> = t.iter().map(|ty| ty.to_value()).collect();
                Value::Tuple(v?)
            }
            SupportedType::Slice(t, len) => {
                let val = t.to_value()?;
                Value::Array(vec![val; len.value()?])
            }

            SupportedType::Fn => {
                return Err("function");
            }
            SupportedType::Var(_) => {
                return Err("unknown generic");
            }
            SupportedType::ImplAny => {
                return Err("unknown type");
            }
        };
        Ok(res)
    }
    pub fn n_arcis_values(&self) -> Result<usize, &'static str> {
        let res = match self {
            SupportedType::Unit => 0,
            SupportedType::FieldValue(_) => 1,
            SupportedType::Bool => 1,
            SupportedType::Curve(_) => 0,
            SupportedType::Struct(s) => s
                .types
                .iter()
                .map(|ty| ty.n_arcis_values())
                .sum::<Result<_, _>>()?,
            SupportedType::Ref(s) => s.n_arcis_values()?,
            SupportedType::Array(s, n) => n.value()? * s.n_arcis_values()?,
            SupportedType::Tuple(types) => types
                .iter()
                .map(|ty| ty.n_arcis_values())
                .sum::<Result<_, _>>()?,

            SupportedType::Slice(s, len) => len.value()? * s.n_arcis_values()?,
            SupportedType::Fn => 0,
            SupportedType::Var(_) => {
                return Err("Uninferred types like generic type parameters cannot be counted.")
            }
            SupportedType::ImplAny => {
                return Err("Uninferred types like `impl X` cannot be counted.")
            }
        };
        Ok(res)
    }
    pub fn n_bools(&self) -> Result<usize, &'static str> {
        let res = match self {
            SupportedType::Unit => 0,
            SupportedType::FieldValue(r) => r.n_bools()?,
            SupportedType::Bool => 1,
            SupportedType::Curve(_) => 0,
            SupportedType::Struct(s) => s
                .types()
                .iter()
                .map(|ty| ty.n_bools())
                .sum::<Result<_, _>>()?,
            SupportedType::Ref(s) => s.n_bools()?,
            SupportedType::Array(s, n) => n.value()? * s.n_bools()?,
            SupportedType::Tuple(types) => {
                types.iter().map(|ty| ty.n_bools()).sum::<Result<_, _>>()?
            }

            SupportedType::Slice(s, len) => len.value()? * s.n_bools()?,
            SupportedType::Fn => 0,
            SupportedType::Var(_) => {
                return Err("Uninferred types like generic type parameters cannot be counted.")
            }
            SupportedType::ImplAny => {
                return Err("Uninferred types like `impl X` cannot be counted.")
            }
        };
        Ok(res)
    }
    pub fn build_from_bools(
        &self,
        rev_values: &mut Vec<BooleanValue>,
        memory: &mut Memory,
    ) -> SimpleAddress {
        match self {
            SupportedType::Unit => Memory::SIMPLE_ADDRESS_OF_UNIT,
            SupportedType::FieldValue(r) => {
                let val = r.build_from_bools(rev_values);
                memory.push(Data::Val(val))
            }
            SupportedType::Bool => {
                let val = rev_values.pop().expect("Not enough values to build bools.");
                memory.push(Data::Bool(BoolData(val)))
            }
            SupportedType::Ref(ty) => {
                let addr = ty.build_from_bools(rev_values, memory);
                memory.push(Data::Pointer(addr.as_pointer()))
            }
            SupportedType::Struct(s) => {
                let v = s
                    .types()
                    .iter()
                    .map(|x| x.build_from_bools(rev_values, memory))
                    .collect();
                memory.push(Data::Collection(v, CollectionKind::Struct(s.clone())))
            }
            SupportedType::Array(ty, n) => {
                let val = n.value().expect("array size is unknown");
                let v = (0..val)
                    .map(|_| ty.build_from_bools(rev_values, memory))
                    .collect();
                memory.push(Data::Collection(v, CollectionKind::Array(*ty.clone())))
            }
            SupportedType::Tuple(types) => {
                let v = types
                    .iter()
                    .map(|ty| ty.build_from_bools(rev_values, memory))
                    .collect();
                memory.push(Data::Collection(v, CollectionKind::Tuple))
            }
            SupportedType::Slice(ty, n) => {
                let len = n.value().expect("Slices cannot be built from bools.");
                SupportedType::Array(ty.clone(), ArrayLen::Computed(len))
                    .build_from_bools(rev_values, memory)
            }
            _ => {
                panic!("Types like `{}` cannot be built from bools.", self)
            }
        }
    }
    pub fn boolean() -> Self {
        SupportedType::Bool
    }
    pub fn u8() -> Self {
        SupportedType::FieldValue(RustType::u8())
    }
    pub fn is_u8(&self) -> bool {
        self == &SupportedType::u8()
    }
    pub fn reference(self) -> Self {
        SupportedType::Ref(Box::new(self))
    }
    pub fn is_reference(&self) -> bool {
        match self.pointer_depth() {
            PointerDepth::FromNonPtr(d) => d > 0,
            PointerDepth::FromAny(d) => d > 0,
        }
    }
    pub fn is_slice(&self) -> bool {
        matches!(self, SupportedType::Slice(..) | SupportedType::Array(..))
    }
    pub fn underlying_type(&self) -> &SupportedType {
        if let SupportedType::Ref(t) = self {
            t.underlying_type()
        } else {
            self
        }
    }
    pub fn compatible_data_types(&self, other: &Self) -> bool {
        match (self, other) {
            (SupportedType::Unit, SupportedType::Unit) => true,
            (SupportedType::FieldValue(s), SupportedType::FieldValue(o)) => s.intersect(*o).is_ok(),
            (SupportedType::Bool, SupportedType::Bool) => true,
            (SupportedType::Curve(s), SupportedType::Curve(o)) => s.intersect(*o).is_ok(),
            (SupportedType::Struct(s), SupportedType::Struct(o)) => s == o,
            (SupportedType::Ref(s), SupportedType::Ref(o)) => s.compatible_data_types(o),
            (
                SupportedType::Array(s, ArrayLen::Computed(s_len)),
                SupportedType::Array(o, ArrayLen::Computed(o_len)),
            ) => s.compatible_data_types(o) && s_len == o_len,
            (SupportedType::Tuple(s), SupportedType::Tuple(o)) => {
                s.len() == o.len()
                    && o.iter()
                        .zip(s.iter())
                        .all(|(a, b)| a.compatible_data_types(b))
            }
            (SupportedType::Fn, SupportedType::Fn) => true,
            _ => {
                if self == other {
                    panic!("Unimplemented `compatible_data_types` for type `{}`", self)
                } else {
                    false
                }
            }
        }
    }
    pub fn allows_tuple_access(&self) -> bool {
        match self {
            SupportedType::Struct(s) => matches!(s.get_kind(), StructKind::Tuple),
            SupportedType::Ref(s) => s.allows_tuple_access(),
            SupportedType::Tuple(_) => true,
            _ => false,
        }
    }
}

impl std::fmt::Display for SupportedType {
    fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> {
        match self {
            SupportedType::Unit => f.write_str("()"),
            SupportedType::FieldValue(r) => write!(f, "{}", r),
            SupportedType::Bool => f.write_str("bool"),
            SupportedType::Curve(r) => write!(f, "{}", r),
            SupportedType::Struct(s) => write!(f, "{}", s),
            SupportedType::Slice(ty, _) => f
                .write_str("[")
                .and_then(|_| write!(f, "{}", ty))
                .and_then(|_| f.write_str("]")),
            SupportedType::Var(s) => {
                write!(f, "Generic {:?}", s)
            }
            SupportedType::ImplAny => f.write_str("impl Any"),
            SupportedType::Ref(a) => f.write_str("&").and_then(|_| a.fmt(f)),
            SupportedType::Array(ty, len) => write!(f, "[{};{}]", ty, len),
            SupportedType::Fn => f.write_str("fn(..) -> _"),
            SupportedType::Tuple(v) => {
                f.write_str("(")?;
                for item in v {
                    write!(f, "{}, ", item)?;
                }
                f.write_str(")")
            }
        }
    }
}
