use serde_derive_internals::{ast, ast::Container as SerdeContainer, attr};

use crate::{attrs::TsifyContainerAttars, ctxt::Ctxt};

pub struct Container<'a> {
    pub ctxt: Ctxt,
    pub attrs: TsifyContainerAttars,
    pub serde_container: SerdeContainer<'a>,
    pub enum_variant_names: Option<Vec<syn::Ident>>,
}

impl<'a> Container<'a> {
    pub fn new(
        serde_container: SerdeContainer<'a>,
        enum_variant_names: Option<Vec<syn::Ident>>,
    ) -> Self {
        let input = &serde_container.original;
        let attrs = TsifyContainerAttars::from_derive_input(input);
        let ctxt = Ctxt::new();

        let attrs = match attrs {
            Ok(attrs) => attrs,
            Err(err) => {
                ctxt.syn_error(err);
                Default::default()
            }
        };

        Self {
            ctxt,
            attrs,
            serde_container,
            enum_variant_names,
        }
    }

    pub fn from_derive_input(input: &'a syn::DeriveInput) -> syn::Result<Self> {
        let enum_variant_names: Option<Vec<_>> = if let syn::Data::Enum(enum_data) = &input.data {
            Some(
                enum_data
                    .variants
                    .iter()
                    .map(|variant| variant.ident.clone())
                    .collect(),
            )
        } else {
            None
        };

        let cx = serde_derive_internals::Ctxt::new();
        let serde_cont =
            SerdeContainer::from_ast(&cx, input, serde_derive_internals::Derive::Serialize);

        match serde_cont {
            Some(serde_container) => {
                cx.check()?;
                Ok(Self::new(serde_container, enum_variant_names))
            }
            None => Err(cx.check().expect_err("serde_cont is None")),
        }
    }

    pub fn ident(&self) -> &syn::Ident {
        &self.serde_container.ident
    }

    #[inline]
    pub fn serde_attrs(&self) -> &attr::Container {
        &self.serde_container.attrs
    }

    pub fn transparent(&self) -> bool {
        self.serde_attrs().transparent()
    }

    pub fn name(&self) -> String {
        self.serde_attrs().name().serialize_name()
    }

    pub fn generics(&self) -> &syn::Generics {
        self.serde_container.generics
    }

    pub fn serde_data(&self) -> &ast::Data {
        &self.serde_container.data
    }

    pub fn syn_error(&self, err: syn::Error) {
        self.ctxt.syn_error(err);
    }

    pub fn check(self) -> syn::Result<()> {
        self.ctxt.check()
    }
}
