use proc_macro::TokenStream;
use quote::quote;
use syn::{Item, parse_macro_input};

use crate::types::{exported_type_from_enum, exported_type_from_struct, register_export_type};

pub(crate) fn expand(attr: TokenStream, item: TokenStream) -> TokenStream {
    if !attr.is_empty() {
        return syn::Error::new_spanned(
            proc_macro2::TokenStream::from(attr),
            "#[export_type] does not accept arguments",
        )
        .into_compile_error()
        .into();
    }

    let item = parse_macro_input!(item as Item);

    match item {
        Item::Struct(item_struct) => {
            let span = item_struct.ident.span();
            match exported_type_from_struct(&item_struct) {
                Ok(def) => match register_export_type(def, span) {
                    Ok(()) => quote! { #item_struct }.into(),
                    Err(err) => err.to_compile_error().into(),
                },
                Err(err) => err.to_compile_error().into(),
            }
        }
        Item::Enum(item_enum) => {
            let span = item_enum.ident.span();
            match exported_type_from_enum(&item_enum) {
                Ok(def) => match register_export_type(def, span) {
                    Ok(()) => quote! { #item_enum }.into(),
                    Err(err) => err.to_compile_error().into(),
                },
                Err(err) => err.to_compile_error().into(),
            }
        }
        other => {
            syn::Error::new_spanned(other, "#[export_type] may only be applied to structs or enums")
                .into_compile_error()
                .into()
        }
    }
}
