use std::convert::TryFrom;

use crate::*;
use syn::{
    Expr, Token,
    parse::{self, Parse, ParseStream},
    punctuated::Punctuated,
};

impl Parse for PayloadAttrs {
    fn parse(input: ParseStream) -> parse::Result<Self> {
        let mut attrs: Vec<PayloadAttr> = vec![];
        for expr in Punctuated::<Expr, Token![,]>::parse_terminated(input)? {
            match expr {
                Expr::Assign(assign) => {
                    let Expr::Path(key) = assign.left.as_ref() else {
                        return Err(syn::Error::new_spanned(assign.left, E::FailExtractIdent));
                    };
                    let key = key
                        .path
                        .get_ident()
                        .ok_or(syn::Error::new_spanned(
                            assign.left.clone(),
                            E::FailExtractIdent,
                        ))?
                        .to_string();
                    if key == PayloadAttrId::Path.to_string() {
                        attrs.push(PayloadAttr::Path(ModulePath::try_from(&*assign.right)?));
                    } else {
                        return Err(syn::Error::new_spanned(assign, E::UnsupportedAttr));
                    }
                }
                Expr::Path(expr) => {
                    if let Some(ident) = expr.path.clone().get_ident() {
                        let as_str = ident.to_string();
                        if as_str == PayloadAttrId::Ctx.to_string() {
                            attrs.push(PayloadAttr::Ctx)
                        } else if as_str == PayloadAttrId::NoDefaultSig.to_string() {
                            attrs.push(PayloadAttr::NoDefaultSig)
                        } else if as_str == PayloadAttrId::Bincode.to_string() {
                            attrs.push(PayloadAttr::Bincode)
                        } else if as_str == PayloadAttrId::Hooks.to_string() {
                            attrs.push(PayloadAttr::Hooks)
                        } else if as_str == PayloadAttrId::NoAutoCrc.to_string() {
                            attrs.push(PayloadAttr::NoAutoCrc)
                        } else if as_str == PayloadAttrId::NoCrc.to_string() {
                            attrs.push(PayloadAttr::NoCrc)
                        } else if as_str == PayloadAttrId::Crypt.to_string() {
                            attrs.push(PayloadAttr::Crypt);
                        }
                    } else {
                        attrs.push(PayloadAttr::Path(ModulePath::from(&expr)));
                    }
                }
                unknown => {
                    return Err(syn::Error::new_spanned(unknown, E::UnsupportedAttr));
                }
            }
        }
        Ok(PayloadAttrs(attrs))
    }
}
