use proc_macro2::TokenStream;
use quote::{quote, ToTokens};
use syn::{braced, punctuated::Punctuated, Token};

use super::Attributes;

#[derive(Clone)]
pub struct ExternType {
    pub attributes: Attributes,
    pub name: syn::Ident,
    pub alias: Option<syn::Type>,
}

pub struct ExternTypeBlock {
    pub types: Punctuated<ExternType, Token![;]>,
}

impl syn::parse::Parse for ExternType {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let attributes = input.parse()?;
        input.parse::<Token![type]>()?;
        let name = input.parse()?;
        let alias = if input.peek(Token![=]) {
            input.parse::<Token![=]>()?;
            Some(input.parse()?)
        } else {
            None
        };
        Ok(Self {
            attributes,
            name,
            alias,
        })
    }
}

impl syn::parse::Parse for ExternTypeBlock {
    fn parse(input: syn::parse::ParseStream<'_>) -> syn::Result<Self> {
        let types;
        braced!(types in input);
        let types = types.parse_terminated(ExternType::parse, Token![;])?;
        Ok(Self { types })
    }
}

impl ToTokens for ExternType {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let Self {
            attributes,
            name,
            alias,
        } = self;
        let alias = alias.as_ref().map(|ty| quote!(= #ty));
        tokens.extend(quote! {
            #attributes
            type #name #alias
        });
    }
}

impl ToTokens for ExternTypeBlock {
    fn to_tokens(&self, tokens: &mut TokenStream) {
        let Self { types } = self;
        tokens.extend(quote! {
            extern {
                #types
            }
        })
    }
}
